thread safe
Dec. 2nd, 2009 01:45 pm![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
в очередной раз убедился, что Eric Lippert is a moron.
moron отличается от нормального человека, в частности, тем, что не способен понять, что написал чушь, а, наоборот, будет отстаивать в комментах чушь, которую написал ранее, даже после того, как ему многократно и вежливо объяснили, в чём он ошибается.
например, в его inline ответах к комменту, датированному "October 20, 2009 8:39 AM", он даже не видит разницы между thread safe Queue.IsEmpty() и не thread safe. "всё равно нужно синхронизировать доступ к Queue во внешнем коде", пишет тупой Эрик, так что без разницы, thread-safe он или нет.
забавно, что даже такую простейшую функциональность, как IsEmpty() можно с пользой использовать без внешней синхронизации. например,
if queue.IsEmpty() Thread.Sleep(SLEEP_INTERVAL);
уж не говоря о более осмысленных конструкциях типа bool TryDequeue(out value);
грустнее всего, что вот такие люди нам пишут .Net 4.0. как его ещё не выгнали, не понимаю.
moron отличается от нормального человека, в частности, тем, что не способен понять, что написал чушь, а, наоборот, будет отстаивать в комментах чушь, которую написал ранее, даже после того, как ему многократно и вежливо объяснили, в чём он ошибается.
например, в его inline ответах к комменту, датированному "October 20, 2009 8:39 AM", он даже не видит разницы между thread safe Queue.IsEmpty() и не thread safe. "всё равно нужно синхронизировать доступ к Queue во внешнем коде", пишет тупой Эрик, так что без разницы, thread-safe он или нет.
забавно, что даже такую простейшую функциональность, как IsEmpty() можно с пользой использовать без внешней синхронизации. например,
if queue.IsEmpty() Thread.Sleep(SLEEP_INTERVAL);
уж не говоря о более осмысленных конструкциях типа bool TryDequeue(out value);
грустнее всего, что вот такие люди нам пишут .Net 4.0. как его ещё не выгнали, не понимаю.
(no subject)
Date: 2009-12-02 10:31 pm (UTC)И появляется вопрос: ты что-нибудь можешь сказать относительно реально случающихся случаев, когда тебе хватает built-in thread-safety?
Вот ты пример привёл, `if queue.IsEmpty() Thread.Sleep(SLEEP_INTERVAL);`, он типа что должен показать? Что ты умеешь реализовать тормозючий как пиздец busy wait (наличие слипа не делает его менее busy wait) поверх тормозючей как пиздец тред-сейф реализации? С привязкой к системной стратегии квантования, которая ВНЕЗАПНО может трахнуть тебя в жопу между делом? Потому что тебе вдруг стало пофиг на предсказуемость олгоритма? И вот всё это ты используешь потому, что тебе чем-то не нравится гораздо более эффективная и несравнимо более надёжная алтернатива в виде `lock (queue)`, где сама очередь совсем не тредсейф?
(no subject)
Date: 2009-12-02 10:46 pm (UTC)А вот как thread-safe queue.IsEmpty может быть полезно для чего бы то ни было, я представить не могу. Она сама её лочит один раз, потом ещё раз лочит когда ты пытаешься декьюйнуть и при этом может обломаться, то есть потратить множество процессорного времени впустую, вместо того, чтобы ты сделал этот один первый лок сам и достал значение, если оно есть, гарантированно. В чём смысл-то?
(no subject)
Date: 2009-12-02 10:47 pm (UTC)(no subject)
Date: 2009-12-02 11:49 pm (UTC)(no subject)
Date: 2009-12-02 11:01 pm (UTC)а уж каким образом lock (queue) будет "гораздо более эффективен" is beyond me. всё строго наоборот, если ты в одном месте использовал lock (queue), то и везде должен будешь его пихать, вместо того, чтобы использовать потенциально более эффективные (потому что имеют доступ к private state) механизмы синхронизации *внутри* класса. например, какой-нибудь ReaderWriterLock (который queue, разумеется, не поможет, но вполне поможет какому-нибудь другому контейнеру, который читается чаще, чем пишется).
про привязку к стратегии квонтования которая трахает в жопу я ваще не понял.
(no subject)
Date: 2009-12-02 11:30 pm (UTC)Ты уже не в первый раз употребляешь это словосочетание, как будто оно является silver bullet, позволяющей не думать о том, что там на самом деле происходит. Это не так.
> например, какой-нибудь ReaderWriterLock
Конечно он не поможет Queue. И ничему вообще из стандартной библиотеки не поможет. Потому что you are supposed to implement it yourself where it's needed! Со своими, блин, локами в своей, блин, реализации, поверх не-тред-сейф очереди или другого контейнера! Потому что никто кроме тебя не знает, что тебе нужен именно такой ReaderWriterLock!
> про привязку к стратегии квонтования которая трахает в жопу я ваще не понял.
Ну как бы это. Если у тебя есть 64 way компутер, например, как ты думаешь, когда там просыпаются треды, которые делали Thread.Sleep(1)? Уж не одновременно ли? Уж не одновременно ли все 64 треда (если ты их столько проспавнил) долго и муторно тусуются в очереди на получение ответа "да, в очереди что-то есть, одно", общего для всех, чтобы потом долго и муторно тусоваться в попытках это что-то оттуда достать, безуспешных для 63 тредов?
А если эти 63 треда потом засыпают на экспоненциально растущее время, готов ли ты объяснить, почему твоя мощная мойшина после тормозной обработки первых надцати запросов вдруг начинает обрабатывать множество последующих в один тред и не справляется с нагрузкой? Ах, все остальные экспоненциально ждут, какое экспоненциально красивое экспоненциальное архитектурное решение!
Каким образом внешний один лок с гарантированным результатом лучше внутренних двух с негарантированным я объяснил в другом комменте.
Алсо, ты так и не ответил на вопрос: ты что-нибудь можешь сказать относительно реально случающихся случаев, когда тебе хватает built-in thread-safety?
(no subject)
Date: 2009-12-03 12:14 am (UTC)это пример простого и эффективного паттерна, позволяющего избежать дурацких вопросов типа "на сколько, собственно, засыпать?"
как всякие простые универсальные решения, оно не является образцом рекордной производительности, а просто good enough. как и всегда, если нужно ставить рекорды, нужно кастомное решение. подавляющему большинству юзеров, которые так раздражают Эрика, кастомного решения не нужно.
> Потому что you are supposed to implement it yourself where it's needed! Со своими, блин, локами в своей, блин, реализации, поверх не-тред-сейф очереди или другого контейнера! Потому что никто кроме тебя не знает, что тебе нужен именно такой ReaderWriterLock!
does not compute. или ты имплементируешь свои собственные thread-safe контейнеры со своим же rw локом, или строишь [но не так эффективно] из готовых кусков, одним из которых является ReaderWriterLock.
> Вот кстати насчёт более осмысленной конструкции типа bool TryDequeue(out value) я согласен, она может быть полезна
...
> А если эти 63 треда потом засыпают на экспоненциально растущее время, готов ли ты объяснить, почему твоя мощная мойшина после тормозной обработки первых надцати запросов вдруг начинает обрабатывать множество последующих в один тред и не справляется с нагрузкой?
IsEmpty как раз полезнее в такой ситуации, чем TryDequeue, потому что позволяет отресетить fallback, а TryDequeue просто говорит "нету ничего".
> Алсо, ты так и не ответил на вопрос: ты что-нибудь можешь сказать относительно реально случающихся случаев, когда тебе хватает built-in thread-safety?
так как раз не хватает, потому что пишут уроды типа липперта. зачем мне Dequeue, который бросает exception? было бы TryDequeue - может и хватало бы. там в комментах, собственно, Rob McCready всё правильно объяснил [а потом объяснил ещё раз, потому что с первого до Эрика не дошло].
(no subject)
Date: 2009-12-03 09:17 pm (UTC)Ну, это, McCready does have a point. But so does Eric.
Но обсуждают они на самом деле совершенно разные вещи.
Эрик говорит про _обычную_ queue (например). У которой есть методы Enqueue, Dequeue, IsEmpty и всё такое. Его поинт состоит в том, что делать _эту_ очередь thread safe совершенно бессмысленно. И он совершенно прав.
Второй чувак говорит про особую очередь, разработанную специально для синхронизации. У неё нет проперти IsEmpty, зато есть методы TryDequeue, Dequeue(TimeSpan timeout) и другие. Возможно даже специальный блокирующийся Enqueue и возможность установить MaxCapacity.
Это совсем другой, высокоуровневый класс, который действительно предоставляет useful methods for inter-thread communication. Этот класс следует рассматривать рядом с такими классами как Mutex, например, а не рядом со стандартной очередью. Вот как ты считаешь, можно ли к классу Mutex осмысленно применить прилагательное thread-safe? Я считаю, что нет, оно там по дефолту подразумевается, а сам он реализует гораздо более мощную абстракцию, которую этим же словом называть наверное не следует всё-таки.
Ещё раз: имеется более или менее техническое определение thread safety, означающее что очередь или хэштейбл или что-нибудь ещё "has defined behaviour, and that behaviour is defined to be inconsistent and timing-dependent" (в случае обычной очереди или хэштейбла). И есть другое, гораздо более мощное и полезное понятие/свойство, описывающее в том числе и предоставляемый интерфейс, "inter-thread communication primitive". Более того, гипотетическая System.Threading.SynchronizedQueue на самом деле является довольно-таки outstanding в этом смысле, потому что действительно обычно не требует никаких дополнительных телодвижений, тогда как представить в такой же степени удобный SynchronizedDictionary мне уже тяжело, то есть понятно, что он должен параметризоваться типом лока, ожидаемой капасити (чтобы лочить только часть внутренних массивов) етс, но вот должен ли у него быть оператор this[]? Должен ли он поддерживать IEnumerable (наверняка нет)? етс.
Да, кажется, Эрик, когда писал свой пост и отвечал на первые два коммента Роба, о подобных высокоуровневых классах не задумывался. Впрочем, и сам Роб got to the point только в третьем комменте.
(no subject)
Date: 2009-12-03 10:31 pm (UTC)inter-thread communication тут ни при чём. unless we count any shared data as "communication".
anyway... весь сыр-бор там начался с того, что Эрик заявил, что thread safety - непонятное и бессмысленное словосочетание. то, что он для иллюстрации своего якобы пойнта взял класс Queue, интерфейс которого мало приспособлен для многопоточности, это проблема исключительно самого Эрика.
на что Роб ему очень внятно и подробно ответил. то, что ты считаешь, что "Роб got to the point только в третьем комменте" - очень грустно. я считаю, что всё было предельно разжёвано уже во втором, после которого никаких вопросов не должно оставаться.
(no subject)
Date: 2009-12-03 11:51 pm (UTC)И только в третьем комменте он наконец собирает всё это в единое целое и говорит, что специальные классы, специально разработанные для IPC, могут быть полезны сами по себе.
(no subject)
Date: 2009-12-04 12:43 am (UTC)по-моему, ты путаешь полезность термина "thread safe" и полезность того, чтобы каждый класс, совершенно без изменения его интерфейса, делать thread safe.
полезность второго никто никогда не утверждал, даже Эрик. полезность первого очевидна мне и Робу, Эрик её отрицает. твоя позиция неясна :)
> для IPC
ну при чём тут нахуй IPC? тебе посчитать, сколько ошибок ты сделал, употребив тут этот термин?
в третьем комменте Роб вовсе не говорит, что специальные thread safe классы бывают иногда полезны. это тривиально и даже Эрику понятно с самого начала. в третьем комменте Роб просто приводит примеры, дополнительно иллюстрирующие то, что и так должно быть к этому моменту понятно уже.
(no subject)
Date: 2009-12-04 09:34 am (UTC)Давай я на отвлечённом примере попробую. Есть такая штука, microkernel OS architecture. Которая на бумаге выглядит очень мило и полезно, а на практике почему-то в полном объёме в успешных продуктах никогда не реализуется, вплоть до того, что OSX использует ядро Mach, которое когда-то было микрокернел, но эппловцы оттуда всю эту фигню выкинули. Так вот, есть такие специальные люди, которые любят заявить, что у микрокернелов есть офигительное достоинство: если вдруг мемори менеджер сдохнет, то его можно будет перезагрузить! "Вы что, мороны, не понимаете, что я говорю?" -- удивляются подобные люди видя отсутствие энтузиазма со стороны слушателей, которые как раз-таки понимают _слишком хорошо_, что если у тебя сдох мемори менеджер, то перегружать нужно весь компьютер, в любом случае. А чтобы например перегружать что-нибудь другое, типа сдохшего видеодрайвера, нужно наворотить вокруг такую огромную и сложную кучу кода, который будет отслеживать и восстанавливать его состояние после перезагрузки (и который сам не будет перезагружабелен), что упоминать вклад архитектуры в это как-то странно, хотя конечно же если бы не она (по крайней мере в этом конкретном месте), то ничего не вышло бы. Типа как вот компьютер без электричества не работает, но уделять внимание и вообще упоминать электричество в контексте разработки какой-нибудь программы наверное не следует.
Вот твоя позиция мне почему-то всё это дико напоминает.
Я не путаю полезность термина "thread safe" и полезность отредсейфливания каждого класса без изменения интерфейса. Я твёрдо уверен, что это одно и то же. Потому что мне и в голову не придёт сказать, что класс Mutex -- thread safe. Несмотря на то, что он таки да. Свойство, которым обладают специальные классы, следует называть иначе. Свойство "to be thread safe" -- само по себе бесполезно. То, что оно есть у специальных полезных классов, ничего не меняет.
(no subject)
Date: 2009-12-02 10:33 pm (UTC)(no subject)
Date: 2009-12-02 11:43 pm (UTC)(no subject)
Date: 2009-12-02 11:52 pm (UTC)(no subject)
Date: 2009-12-03 06:24 am (UTC)(no subject)
Date: 2009-12-03 08:35 am (UTC)(no subject)
Date: 2009-12-03 06:50 am (UTC)Вам там недовешивают синхронизированных классов в стандартной библиотеке?
Чисто практически (по опыту с Java) всякие concurrency facilities (ex: blocking queue), конечно, удобно иметь safe, а вот уже что-нибудь уже типа коллекций -- не так чтоб сильно.
(no subject)
Date: 2009-12-03 08:38 am (UTC)что такое blocking queue?
да не особенно задела. я просто наткнулся на липперта на stackoverflow.com и пошёл читать его блог на msdn, забыв, что я уже в своё время открыл для себя, что он мудак. времени жалко :)
(no subject)
Date: 2009-12-03 02:18 pm (UTC)вообще мне нравился подход с thread-safe врапперами, в 1.1. как-то они его зря в 2.0 не сделали.
(no subject)
Date: 2009-12-03 10:32 pm (UTC)(no subject)
Date: 2009-12-03 10:40 pm (UTC)