thread safe

Dec. 2nd, 2009 01:45 pm
109: (Default)
[personal profile] 109
в очередной раз убедился, что 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. как его ещё не выгнали, не понимаю.

(no subject)

Date: 2009-12-02 10:31 pm (UTC)
From: [identity profile] faceted-jacinth.livejournal.com
Ну. Он там конечно местами перегибает палку, но его поинт же вот в чём:
Though I suppose that's [thread-safety] a nice property to have, does it really matter whether an object has that property if in order to use any of its basic methods, you're going to have to synchronize access to it exactly as though it did not armor its internals against race conditions?


И появляется вопрос: ты что-нибудь можешь сказать относительно реально случающихся случаев, когда тебе хватает 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)
From: [identity profile] faceted-jacinth.livejournal.com
Вот кстати насчёт более осмысленной конструкции типа bool `TryDequeue(out value);` я согласен, она может быть полезна. Хотя правильней было бы всё равно явно синхронизироваться через эвенты, ну да ладно, эта штука будет обычно работать и даже не очень неэффективно (пока перед тобой не встанет вопрос о том, на сколько, собственно, засыпать).

А вот как thread-safe queue.IsEmpty может быть полезно для чего бы то ни было, я представить не могу. Она сама её лочит один раз, потом ещё раз лочит когда ты пытаешься декьюйнуть и при этом может обломаться, то есть потратить множество процессорного времени впустую, вместо того, чтобы ты сделал этот один первый лок сам и достал значение, если оно есть, гарантированно. В чём смысл-то?

(no subject)

Date: 2009-12-02 10:47 pm (UTC)
From: [identity profile] faceted-jacinth.livejournal.com
И да, TryDequeue можно и нужно реализовывать экстенжен методом для not thread-safe queue. Ммм?

(no subject)

Date: 2009-12-02 11:49 pm (UTC)
From: [identity profile] 109.livejournal.com
разумеется, нет. нельзя один метод сделать thread-safe, а остальные оставить не-safe. плюс локи, если через них делается, надо брать на private syncObject, а то я видел, как умельцы делают lock (this).

(no subject)

Date: 2009-12-02 11:01 pm (UTC)
From: [identity profile] 109.livejournal.com
во-первых, Thread.Sleep is not a busy wait. it's kind of an opposite of busy wait, if you know what I mean. пример мой intentionally примитивен; в настоящем коде, понятное дело, нужен какой-нибудь exponential fallback. который тем не менее таки будет использовать IsEmpty безо всякой дополнительной синхронизации.

а уж каким образом lock (queue) будет "гораздо более эффективен" is beyond me. всё строго наоборот, если ты в одном месте использовал lock (queue), то и везде должен будешь его пихать, вместо того, чтобы использовать потенциально более эффективные (потому что имеют доступ к private state) механизмы синхронизации *внутри* класса. например, какой-нибудь ReaderWriterLock (который queue, разумеется, не поможет, но вполне поможет какому-нибудь другому контейнеру, который читается чаще, чем пишется).

про привязку к стратегии квонтования которая трахает в жопу я ваще не понял.

(no subject)

Date: 2009-12-02 11:30 pm (UTC)
From: [identity profile] faceted-jacinth.livejournal.com
> нужен какой-нибудь exponential fallback
Ты уже не в первый раз употребляешь это словосочетание, как будто оно является 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)
From: [identity profile] 109.livejournal.com
> Ты уже не в первый раз употребляешь это словосочетание, как будто оно является silver bullet, позволяющей не думать о том, что там на самом деле происходит. Это не так.

это пример простого и эффективного паттерна, позволяющего избежать дурацких вопросов типа "на сколько, собственно, засыпать?"

как всякие простые универсальные решения, оно не является образцом рекордной производительности, а просто 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)
From: [identity profile] faceted-jacinth.livejournal.com
Перечитал дискуссию на свежую голову.

Ну, это, 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)
From: [identity profile] 109.livejournal.com
> inter-thread communication primitive

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)
From: [identity profile] faceted-jacinth.livejournal.com
Да нет же, вот я специально перечитал второй его коммент, и он completely misses the point. Перечитай его тоже! Он ничего не говорит про особые классы, для которых thread safe действительно означает что-то полезное (и перестаёт быть валидным как обозначение, ИМХО). Только в третьем комменте он это обозначает явно. До этого он тоже пиздел на отвлечённые темы, типа что thread safe на любом классе даёт нам множество полезной информации, на что закономерно получил отлуп от Эрика насчёт учёных, которым лучше бы хаскелем заниматься, и тут Эрик ещё вежливый был, я бы на его месте за полезную информацию regardless сразу нахуй послал бы.

И только в третьем комменте он наконец собирает всё это в единое целое и говорит, что специальные классы, специально разработанные для IPC, могут быть полезны сами по себе.

(no subject)

Date: 2009-12-04 12:43 am (UTC)
From: [identity profile] 109.livejournal.com
перечитал твой этот коммент два раза, но смысла словосочетания "thread safe на любом классе даёт нам множество полезной информации" так и не понял.

по-моему, ты путаешь полезность термина "thread safe" и полезность того, чтобы каждый класс, совершенно без изменения его интерфейса, делать thread safe.

полезность второго никто никогда не утверждал, даже Эрик. полезность первого очевидна мне и Робу, Эрик её отрицает. твоя позиция неясна :)

> для IPC

ну при чём тут нахуй IPC? тебе посчитать, сколько ошибок ты сделал, употребив тут этот термин?

в третьем комменте Роб вовсе не говорит, что специальные thread safe классы бывают иногда полезны. это тривиально и даже Эрику понятно с самого начала. в третьем комменте Роб просто приводит примеры, дополнительно иллюстрирующие то, что и так должно быть к этому моменту понятно уже.

(no subject)

Date: 2009-12-04 09:34 am (UTC)
From: [identity profile] faceted-jacinth.livejournal.com
Я перестал понимать, что ты имеешь в виду. Потому что я вроде бы прямо пишу, что, как мне кажется, ты имеешь в виду и что по факту сказал Роб (в первых двух комментах), ты говоришь, что не понял смысла словосочетания и тут же этот смысл пересказываешь своими словами, говоря, что это типа должно быть очевидно.

Давай я на отвлечённом примере попробую. Есть такая штука, microkernel OS architecture. Которая на бумаге выглядит очень мило и полезно, а на практике почему-то в полном объёме в успешных продуктах никогда не реализуется, вплоть до того, что OSX использует ядро Mach, которое когда-то было микрокернел, но эппловцы оттуда всю эту фигню выкинули. Так вот, есть такие специальные люди, которые любят заявить, что у микрокернелов есть офигительное достоинство: если вдруг мемори менеджер сдохнет, то его можно будет перезагрузить! "Вы что, мороны, не понимаете, что я говорю?" -- удивляются подобные люди видя отсутствие энтузиазма со стороны слушателей, которые как раз-таки понимают _слишком хорошо_, что если у тебя сдох мемори менеджер, то перегружать нужно весь компьютер, в любом случае. А чтобы например перегружать что-нибудь другое, типа сдохшего видеодрайвера, нужно наворотить вокруг такую огромную и сложную кучу кода, который будет отслеживать и восстанавливать его состояние после перезагрузки (и который сам не будет перезагружабелен), что упоминать вклад архитектуры в это как-то странно, хотя конечно же если бы не она (по крайней мере в этом конкретном месте), то ничего не вышло бы. Типа как вот компьютер без электричества не работает, но уделять внимание и вообще упоминать электричество в контексте разработки какой-нибудь программы наверное не следует.

Вот твоя позиция мне почему-то всё это дико напоминает.

Я не путаю полезность термина "thread safe" и полезность отредсейфливания каждого класса без изменения интерфейса. Я твёрдо уверен, что это одно и то же. Потому что мне и в голову не придёт сказать, что класс Mutex -- thread safe. Несмотря на то, что он таки да. Свойство, которым обладают специальные классы, следует называть иначе. Свойство "to be thread safe" -- само по себе бесполезно. То, что оно есть у специальных полезных классов, ничего не меняет.

(no subject)

Date: 2009-12-02 10:33 pm (UTC)
From: [identity profile] asy.livejournal.com
во втором абзаце нет ли у нетя недостающего "не"?

(no subject)

Date: 2009-12-02 11:52 pm (UTC)
From: [identity profile] asy.livejournal.com
о! значит ты не морон! %)

(no subject)

Date: 2009-12-03 06:24 am (UTC)
From: [identity profile] novoxudonosser.livejournal.com
А по моему он абсолютно прав. Он же приводит свой наивный пример, в котором безопасная функция IsEmpty и другая безопасная функция Peek вместе не работают, он же не утверждает, что не работает любая комбинация безопасных функций. И вообще он уже много раз говорил о immutable структурах и в этой заметке тоже о них, показывая, что обеспечить много-потоковую безопасность внутренних структур класса, отвечающего на вопросы о своем состоянии в момент, когда вопрос был задан, недостаточно ибо не решает проблему, а откладывает ее, заставляя тем самым своего клиента опять решать ту же проблему но уже на своем уровне. И единственно возможное решение - это получать "фотографии" состояния системы и отвечать на вопросы относительно одной из этих фотографий а не живой системы. Удивительно, но такой подход применим к очень широкому кругу задач.

(no subject)

Date: 2009-12-03 08:35 am (UTC)
From: [identity profile] 109.livejournal.com
с этим я согласен (насчёт immutable), но ничего этого в посте нет. а есть непонимание того, что такое thread safety и как её инкапсулировать.

(no subject)

Date: 2009-12-03 06:50 am (UTC)
From: [identity profile] anton-solovyev.livejournal.com
А почему тебя эта дискуссия так задела?

Вам там недовешивают синхронизированных классов в стандартной библиотеке?

Чисто практически (по опыту с Java) всякие concurrency facilities (ex: blocking queue), конечно, удобно иметь safe, а вот уже что-нибудь уже типа коллекций -- не так чтоб сильно.

(no subject)

Date: 2009-12-03 08:38 am (UTC)
From: [identity profile] 109.livejournal.com
queue - это тоже коллекция :)
что такое blocking queue?

да не особенно задела. я просто наткнулся на липперта на stackoverflow.com и пошёл читать его блог на msdn, забыв, что я уже в своё время открыл для себя, что он мудак. времени жалко :)

(no subject)

Date: 2009-12-03 02:18 pm (UTC)
From: [identity profile] softmaster.livejournal.com
а вдруг ты на queue семафор захочешь повесить - зачем тебе оверхед от внутренней реализации?
вообще мне нравился подход с thread-safe врапперами, в 1.1. как-то они его зря в 2.0 не сделали.

(no subject)

Date: 2009-12-03 10:32 pm (UTC)
From: [identity profile] 109.livejournal.com
а как там это было сделано? всё обёрнуто в lock (this), небось?

(no subject)

Date: 2009-12-03 10:40 pm (UTC)
From: [identity profile] softmaster.livejournal.com
Вроде обычный lock(SyncRoot), но радовало не это, а собственно совместимость между врапперами и обычными контейнерами - враппер можно было сунуть куда угодно. правда, не радовали все виртуальные методы.

Profile

109: (Default)
109

March 2019

S M T W T F S
     12
3456789
101112131415 16
17181920212223
24252627282930
31      

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags