ReaderWriterLockSlim имеет проблемы с грубыми прерываниями потоков
ReaderWriterLockSlim имеет проблемы на серверных конфигурациях (>1 физического процессора)
Краткая переписка с Jeffrey Richter в стадии изучения его библиотеки Power Threading, классы которой не работают даже на моем домашнем Core i5 2500k.
Посему вопрос к сообществу: сталикивались ли вы с подобным поведением и подтверждаете ли тезисы, изложенные в статьях и здесь.
Здравствуйте, Аноним, Вы писали:
А>Посему вопрос к сообществу: сталикивались ли вы с подобным поведением и подтверждаете ли тезисы, изложенные в статьях и здесь.
Сталкивались. Наблюдали большое число System.Threading.ReaderWriterCount в Gen2 под нагрузкой. Пока забили.
Re: ReaderWriterLockSlim problems
От:
Аноним
Дата:
14.12.13 14:40
Оценка:
Здравствуйте, Аноним, Вы писали:
Почему Lock нельзя просто поместить в блок Try
var lockIsHeld = false;
try {
rwl.EnterReadLock();
lockIsHeld = true;
// Do work here
}
finally {
if (lockIsHeld)
rwl.ExitReadLock();
}
Здравствуйте, Аноним, Вы писали:
А>ReaderWriterLockSlim имеет проблемы с грубыми прерываниями потоков
А кто их не имеет-то ??? Любой некооперативный механизм "прерывания" исполнения потока, в общем случае, портит разделяемое состояние.
А>ReaderWriterLockSlim имеет проблемы на серверных конфигурациях (>1 физического процессора)
Он их может и имеет, но вот анализ автора противоречит тому, что вижу лично я.
В декомпилированном ReSharper'ом коде ReaderWriterLockSlim, и с ExitMyLock(), и с инкрементом owners всё в порядке. В первом случае вызывается Volatile.Write(), а во втором все манипуляции выполняются внутри lock'а. Но код мутный. Есть куча внутренних setter'ов для owners, мне было лень смотреть все ли их вызовы внутри lock'ов.
Также у автора зело хитрое железо. Многопроцессорные штуки на семействе Xeon E5-2600 являются NUMA-системами. Согласно документации, там вроде всё должно быть когерентно, но запросто могут быть разложены какие-нибудь тонкие грабли.
А>Краткая переписка с Jeffrey Richter
Здравствуйте, Аноним, Вы писали:
А>Посему вопрос к сообществу: сталикивались ли вы с подобным поведением и подтверждаете ли тезисы, изложенные в статьях и здесь.
Автор создал себе офигенные проблемы уже в момент использования thread.Abort(), всё остальное — уже последствия. Расписывать подробно особого смысла нет, достаточно оф. документации:
There is also a chance that a static constructor could be aborted. In rare cases, this might prevent instances of that class from being created in that application domain.
Если вас устраивают такие грабли в продакшне — то и всё остальное пугать не должно.
Здравствуйте, Sinix, Вы писали:
S>Здравствуйте, Аноним, Вы писали:
А>>Посему вопрос к сообществу: сталикивались ли вы с подобным поведением и подтверждаете ли тезисы, изложенные в статьях и здесь.
S>Автор создал себе офигенные проблемы уже в момент использования thread.Abort(), всё остальное — уже последствия. Расписывать подробно особого смысла нет, достаточно
Ээээ на этом месте по подробнее можно?
thread.Abort используется в куче мест самим фрейсворком.
Здравствуйте, Tom, Вы писали:
Tom>Здравствуйте, Sinix, Вы писали:
S>>Автор создал себе офигенные проблемы уже в момент использования thread.Abort(), всё остальное — уже последствия. Расписывать подробно особого смысла нет, достаточно Tom>Ээээ на этом месте по подробнее можно? Tom>thread.Abort используется в куче мест самим фрейсворком.
Я бы не сказал что в куче
Thread.Abort used by
System.Media.SoundPlayer.LoadSync()
System.Media.SoundPlayer.SetupSoundLocation(string)
System.Media.SoundPlayer.SetupStream(Stream)
System.ServiceModel.ComIntegration.DllHostInitializeWorker.Startup(IProcessInitControl)
Thread.Abort(object) used by
System.Web.HttpResponse.AbortCurrentThread()
System.Web.RequestTimeoutManager.RequestTimeoutEntry.TimeoutIfNeeded(DateTime)
Возможно куча пролетела мимо, но в рефлекторе на момент анализа было порядка 20и сборок фреймворка.
Здравствуйте, Tom, Вы писали:
S>>Автор создал себе офигенные проблемы уже в момент использования thread.Abort(), всё остальное — уже последствия. Расписывать подробно особого смысла нет, достаточно Tom>Ээээ на этом месте по подробнее можно?
Thread.Abort() — это метод отчаяния. Точно так же, как catch {}, он не решает исходную проблему, а маскирует её. Необходимость насильственного прерывания потока означает, что мы _уже_ не можем контролировать его выполнение, и, как следствие, не можем гарантировать корректность его состояния.
Если поток разделяет свой состояние с остальными потоками — всё ещё хуже: убийство потока может поломать всё приложение. Конечно, можно начать защищаться: добавлять cer, не вызывать "неустойчивые" методы из BCL, писать для всех выделяемых ресурсов финализаторы (например, чтобы если что, освободить "утёкшую" блокировку) etc. По сути, мы получим более продвинутый API для всё той же проблемы — прерывания выполнения в любой точке приложения. Разумеется, со своими тараканами: непредсказуемым порядком освобождения ресурсов, повышенной нагрузкой на финализатор, и, главное, с нерешённой исходной проблемой — см. пример с static-конструкторами из документации. Их-то никак не защитишь
Для более-менее свежего кода, который по полной использует task/await (да даже threadpool), Thread.Abort является чистой диверсией. С учётом штатного CancellationToken — ещё и ненужной.
Tom>thread.Abort используется в куче мест самим фрейсворком.
samius написал выше — всего в паре мест И то, как минимум в SoundPlayer от Abort() точно можно избавиться.
Здравствуйте, Sinix, Вы писали:
S>Здравствуйте, Tom, Вы писали:
S>>>Автор создал себе офигенные проблемы уже в момент использования thread.Abort(), всё остальное — уже последствия. Расписывать подробно особого смысла нет, достаточно Tom>>Ээээ на этом месте по подробнее можно?
S>Thread.Abort() — это метод отчаяния. Точно так же, как catch {}, он не решает исходную проблему, а маскирует её.
Это с кокого такого перепугу такое делается заявление?
Отчаяние это TerminateThread а Thread.Abort это более чем кошерное решение проблемы с закрытием и освобождение ресурсов.
S>Необходимость насильственного прерывания потока означает, что мы _уже_ не можем контролировать его выполнение, и, как следствие, не можем гарантировать корректность его состояния.
А с чего ты взял что ты вообще поток контролировал.
Представь себя на месте II&ASP.NET или того же WCF.
Например: Случился таймаут выполнения метода WCF. Вопрос, что делать.
Оставлсять всё так как и есть, т.е. продолжить методу висесть или попытаться его заабортить...?
Или другой случай, случился ресайклинг у IIS App Pool-а, опять же что делать с зависшими запросами....
Здравствуйте, samius, Вы писали:
S>Здравствуйте, Tom, Вы писали:
Tom>>Здравствуйте, Sinix, Вы писали:
S>>>Автор создал себе офигенные проблемы уже в момент использования thread.Abort(), всё остальное — уже последствия. Расписывать подробно особого смысла нет, достаточно Tom>>Ээээ на этом месте по подробнее можно? Tom>>thread.Abort используется в куче мест самим фрейсворком. S>Я бы не сказал что в куче
А что код фрефмворка обязан вызывать Thread.Abort что бы заабортить поток?
Здравствуйте, Tom, Вы писали:
Tom>Здравствуйте, samius, Вы писали:
S>>Здравствуйте, Tom, Вы писали:
Tom>>>thread.Abort используется в куче мест самим фрейсворком. S>>Я бы не сказал что в куче Tom>А что код фрефмворка обязан вызывать Thread.Abort что бы заабортить поток?
Вообще говоря, не обязан. Но я опровергал ваше утверждение о том что thread.Abort используется в куче мест самим фреймворком.
Здравствуйте, Tom, Вы писали:
Tom>Здравствуйте, samius, Вы писали:
Tom>А что код фрефмворка обязан вызывать Thread.Abort что бы заабортить поток?
Не знаю, что код фреймворка вызывает что бы заабортить поток, но вот мнение Липперта относительно Abort-а
In short, Thread.Abort is at best indicative of bad design, possibly unreliable, and extremely dangerous. It should be avoided at all costs; the only time you should ever even consider aborting a thread is in some sort of "emergency shutdown" code where you are attempting to tear down an appdomain as cleanly as possible. и еще
>Are there any other posibility to terminate threads?
Yes. Your problem is that you should never start up a thread that you cannot tell politely to stop, and it stops in a timely manner. If you are in a situation where you have to start up a thread that might be (1) hard to stop, (2) buggy, or worst of all (3) hostile to the user, then the right thing to do is to make a new process, start the thread in the new process, and then terminate the process when you want the thread to go down. The only thing that can guarantee safe termination of an uncooperative thread is the operating system taking down its entire process.
Полагаю что все-таки фреймворк не так часто абортит потоки, как вам хочется.
Здравствуйте, drol, Вы писали:
D>Здравствуйте, Аноним, Вы писали:
А>>ReaderWriterLockSlim имеет проблемы с грубыми прерываниями потоков
D>А кто их не имеет-то ??? Любой некооперативный механизм "прерывания" исполнения потока, в общем случае, портит разделяемое состояние.
А>>ReaderWriterLockSlim имеет проблемы на серверных конфигурациях (>1 физического процессора)
D>Он их может и имеет, но вот анализ автора противоречит тому, что вижу лично я.
D>В декомпилированном ReSharper'ом коде ReaderWriterLockSlim, и с ExitMyLock(), и с инкрементом owners всё в порядке. В первом случае вызывается Volatile.Write(), а во втором все манипуляции выполняются внутри lock'а. Но код мутный. Есть куча внутренних setter'ов для owners, мне было лень смотреть все ли их вызовы внутри lock'ов.
В каком здесь месте нарисовался Volatile.Write? Передавайте привет декомпилерам, они такие классные, особенно бесплатные.
D>Также у автора зело хитрое железо. Многопроцессорные штуки на семействе Xeon E5-2600 являются NUMA-системами. Согласно документации, там вроде всё должно быть когерентно, но запросто могут быть разложены какие-нибудь тонкие грабли.
Там не NUMA, там мало памяти (всего 16 Гб).
А>>Краткая переписка с Jeffrey Richter
D>А переписка-то где ??? Что-то не по глазам...
Ничего полезного. Рихтер предлагает свою библиотеку, она не рабочая.
Контейнеры приложений без абортов вообще сложно представить.
Tom>>Например: Случился таймаут выполнения метода WCF. Вопрос, что делать. Tom>>Оставлсять всё так как и есть, т.е. продолжить методу висесть или попытаться его заабортить...? S>Во-первых, поток не является единицей изоляции. Прибивать надо AppDomain/хост-процесс. S>Во-вторых, в твоих сценариях не предполагается продолжение работы остальных (зависящих от текущего) потоков. У автора исходной статьи такая надежда была S>Ну, и в-третьих, твои сценарии предполагают чистые (утилитарные) потоки без разделяемого состояния. ReaderWriterLockSlim намекает на полностью противоположное.
Здравствуйте, Tom, Вы писали:
S>>>>Автор создал себе офигенные проблемы уже в момент использования thread.Abort(), всё остальное — уже последствия. Расписывать подробно особого смысла нет, достаточно Tom>>>Ээээ на этом месте по подробнее можно?
S>>Thread.Abort() — это метод отчаяния. Точно так же, как catch {}, он не решает исходную проблему, а маскирует её. Tom>Это с кокого такого перепугу такое делается заявление? Tom>Отчаяние это TerminateThread а Thread.Abort это более чем кошерное решение проблемы с закрытием и освобождение ресурсов.
Разница меджу TerminateThread и Thread.Abort в том, что первый гарантированно приведет к проблемам, а второй — лишь в некоторых случаях.
Проблема в том, что Thread.Abort может прервать исполнение практически любой управляемой инструкции, а это значит, что без хитрых хитростей мы можем захватить ресурс, но не спеть сохранить на него хэндл в переменной. Есть ли вообще уверенность, что в случае вызова Thread.Abort будут корректно освобождаться ресурсы в блоке using? Нет, таких гарантий нет, поскольку обеспечить их очень сложно, а проверить — еще сложнее.
Помимо приведенных ссылок, вот тут тоже можно чутка подробнее об этом почитать: О вреде вызова Thread.Abort.
Tom>Или другой случай, случился ресайклинг у IIS App Pool-а, опять же что делать с зависшими запросами....
Когда мы точно знаем, что за код исполняется потоком, то все ОК, но в *общем случае* гарантировать корректное состояние программы после Thread.Abort нельзя.
Здравствуйте, Sinix, Вы писали:
S>Здравствуйте, Tom, Вы писали:
Tom>>Это с кокого такого перепугу такое делается заявление? S>Ок, а как ещё назвать решение прерывать поток выполнения в произвольном месте?
Как угодно. Если это операция безопасна то не вижу проблем. С ThreadAbort и нормальными руками это вполне безопасно.
Tom>>Отчаяние это TerminateThread а Thread.Abort это более чем кошерное решение проблемы с закрытием и освобождение ресурсов. S>Как раз с "кошерным" освобождением ресурсов _в общем случае_ у Thread.Abort большие проблемы. Первые же ссылки из гугла
Это конечно круто взять ссылки из гугла и кинуть их сюда.
Это конечно же доказывает многое...
Проблемы с ThreadAbort у людей бывают когда они не понимают что это такое, когда выбрасывается и что с ним вообще делать надо.
Это мне напоминает мнение когда люди считают проблемой — любое исключение не понимая что исключение это просто механизм, инструмент в их руках.
Причём этим инструментом можно себе дырку в ноге прокалупать или можно построить что то хорошее...
Tom>>Например: Случился таймаут выполнения метода WCF. Вопрос, что делать. Tom>>Оставлсять всё так как и есть, т.е. продолжить методу висесть или попытаться его заабортить...? S>Во-первых, поток не является единицей изоляции. Прибивать надо AppDomain/хост-процесс.
Очень интересно. Т.е. если у меня таймаут при вызове веб сервиса мне надо домен прибить?
Или ещё круче аж процесс? Может проще винду перегрузить или даже диск отформатировать...
S>Во-вторых, в твоих сценариях не предполагается продолжение работы остальных (зависящих от текущего) потоков. У автора исходной статьи такая надежда была S>Ну, и в-третьих, твои сценарии предполагают чистые (утилитарные) потоки без разделяемого состояния.
Ээээ, опять же с какого перепугу???
Здравствуйте, samius, Вы писали:
S>Здравствуйте, Tom, Вы писали:
Tom>>Здравствуйте, samius, Вы писали:
Tom>>А что код фрефмворка обязан вызывать Thread.Abort что бы заабортить поток? S>Не знаю, что код фреймворка вызывает что бы заабортить поток, но вот мнение Липперта относительно Abort-а S> S>In short, Thread.Abort is at best indicative of bad design, possibly unreliable, and extremely dangerous. It should be avoided at all costs; the only time you should ever even consider aborting a thread is in some sort of "emergency shutdown" code where you are attempting to tear down an appdomain as cleanly as possible.
Вы будете удивлены но я абсолютно с ним согласен. Что абсолютно не значит что ThreadAbort зло во всех случаях.
Есть целый класс случаев когда без него просто невозможно обойтись. Это случаи когда вы не владеете кодом который выполняется в вашем потоке в данную минуту.
Примеры ASP.NET, WCF, SQL Server & CLR.
Во всех этих примерах применяется ThreadAbort
ST>Разница меджу TerminateThread и Thread.Abort в том, что первый гарантированно приведет к проблемам, а второй — лишь в некоторых случаях.
Разница там как между взрывом атомной бомбы и игрой детей с зажигалкой
дети с зажигалкой конечно могут проблем наделать но только если они дети несмышлёные.
ST>Проблема в том, что Thread.Abort может прервать исполнение практически любой управляемой инструкции, а это значит, что без хитрых хитростей мы можем захватить ресурс, но не спеть сохранить на него хэндл в переменной.
В чём проблема сохранить в переменной а потом захватить ресурс?
ST>Есть ли вообще уверенность, что в случае вызова Thread.Abort будут корректно освобождаться ресурсы в блоке using? Нет, таких гарантий нет, поскольку обеспечить их очень сложно, а проверить — еще сложнее.
Если желание и понимание есть то сделать можно. Если понимания нет то проблемы с юзингом можно получить и без abort-а. Элементарный случай — исключение в конструкторе.
ST>Помимо приведенных ссылок, вот тут тоже можно чутка подробнее об этом почитать: О вреде вызова Thread.Abort.
Сергей я вкурсе твоего блога
Tom>>Или другой случай, случился ресайклинг у IIS App Pool-а, опять же что делать с зависшими запросами.... ST>Когда мы точно знаем, что за код исполняется потоком, то все ОК, но в *общем случае* гарантировать корректное состояние программы после Thread.Abort нельзя.
Я об этом и говорю, нельзя взять и агульно назвать Thread.Abort злом.
Те кто понимает насколько огромный это шаг по сравнению с TerminateThread особенно это понимают.
Ну и как я уже говорил ранее есть случаи где без Thread.Abort невозможно обойтись в принципе.