2014-11-12 4 views
0

В обсуждении метода Dispose в CLR Джеффри Рихтера через C#. В статье указывается:Dispose не должен быть потокобезопасным

Однако в директивах по дизайну указано, что Dispose не должен быть потокобезопасным. Причина в том, что код должен вызывать Dispose только в том случае, если код знает за то, что ни один другой поток не использует этот объект.

Это звучит противоречиво для меня. Разумеется, если класс обертывает собственный или управляемый ресурс, как только вызывается Dispose, он должен убедиться, что ни один другой вызывающий абонент не использует этот ресурс одновременно с попыткой освободить ресурс. Мне кажется, что оболочка, зная, сколько абонентов использует ресурс, гораздо проще выполняет синхронизацию Dispose, чем все разные абоненты, которые могут не знать друг друга. Были похожие вопросы here и here, но на самом деле у них нет окончательного ответа на этот вопрос.

Какова обоснованность этого руководства по дизайну?

+1

Как вы это видите? Один поток, использующий объект во время его размещения? Или два объекта, удаляющих один и тот же объект? И для меня это не имеет смысла. –

+2

Ваше предположение, что оболочка может легко узнать, какой код содержит ссылку на нее, является полностью ложной. Такие знания требуют помощи от кода клиента, подсчет ссылок является стандартным подходом. Но это просто заменяет один контракт другим, который может сбой так же легко, если код клиента искажает требования. Утечки - это стандартная ошибка подсчета ссылок, которую очень сложно исправить. –

+0

@PatrickHofman Если один поток использует объект, а другой поток пытается его уничтожить, метод dispose может вернуть исключение, которое используется в этом ресурсе. Или он может вернуть вызов для вызова, когда ресурс будет выпущен. Другой сценарий одновременного использования двух объектов легче обрабатывать, поскольку метод dispose должен допускать множественные вызовы без побочных эффектов. –

ответ

0

Ключевой момент, который часто упускается из виду с Dispose, что его цель состоит в том, чтобы не делать ничего, чтобы объект расположены, а скорее, чтобы объект расположены не уведомлять внешние объекты, что их услуги больше не требуются , Существует много ситуаций, в которых внешняя сущность может быть разделена многими объектами, которые используются разными потоками. Во многих случаях, таким образом, важно, чтобы внешний объект мог одновременно обрабатывать несколько уведомлений или запросов от разных потоков. Если внешний объект использует пул для управления ресурсами, ему может не потребоваться беспокоиться о одновременных действиях, выполняемых над конкретным элементом пула, но он должен быть подготовлен к тому, что он может получать несколько одновременных запросов на размещение или выпуск элементов из/в тот же пул.

Второй момент заключается в том, что некоторые виды коммуникационных библиотек не имеют каких-либо неблокирующих методов и поддерживают ровно один многопоточный сценарий: действие, которое блокирует, может быть асинхронно прервано любым потоком. Прерывание действия таким образом оставит канал в неопределенном состоянии; канал снова не будет использоваться до тех пор, пока он не будет закрыт и не будет вновь открыт. Поскольку такой прерывание покинет канал в бесполезном состоянии, нет причин продолжать удерживать какие-либо ресурсы, связанные с ним после выдачи прерывания. Следовательно, общей и полезной моделью является использование самого Dispose для выполнения прерывания. Такое прерывание должно выполняться из потока, отдельно от основного потока объекта [если основной поток объекта заблокирован, он не может запускать какой-либо код пользователя для запуска прерывания!], Он должен быть потокобезопасным. Обратите внимание, что фактическое поведение очистки может не обязательно выполняться в потоке, который вызывает Dispose. Если вызывается Dispose, когда объект является «запущенным» кодом в своем основном потоке (или такой код заблокирован в рамках одного из методов объекта), он может установить флаг, чтобы заставить его основной поток выполнять очистку; если он вызывается, когда собственный поток объекта не выполняет свой код, объект может позволить потоку, который вызывает Dispose, стать его «основным потоком», который затем может его очистить; если его прежний «основной поток» пытается что-то сделать во время очистки, такое действие немедленно прекратится.

Хотя в большинстве случаев Dispose не следует вызывать объект, пока он все еще используется, есть ситуации, подобные выше, где это будет представлять собой необходимый сценарий использования. Yanking ресурс из-под потребителя может быть довольно грубым способом заставить этого потребителя отключиться, но во многих случаях он может быть более безопасным, чем любая альтернатива.Кроме того, даже в тех случаях, когда объект не должен быть удален, но в любом случае, метод Dispose должен стремиться ограничить вред, который может вызвать такое удаление. Это нормально, если вызов Dispose на объект, пока он все еще используется, приводит к непредвиденным сбоям в работе объекта. Это не так хорошо, если вызов Dispose на одном объекте, который все еще используется, приводит к тому, что следующий созданный объект получает ресурсы, которые первый объект все еще используя. Независимо от того, ведет ли он Dispose по-настоящему безопасным потоком, он должен обеспечить, чтобы вред, причиненный ненадлежащим использованием, ограничивался размещаемым объектом.

+0

Я вижу. Спасибо за Ваш ответ. Что вы думаете об этом заявлении конкретно: «код должен вызывать Dispose, только если код знает, что ни один другой поток не использует этот объект». –

+0

@FarhadAlizadehNoori: Я бы перефразировал его как «Код должен вызывать« Dispose », только если он знает, что любой поток, который может использовать объект *, должен немедленно прекратить * и что контракты любых потоков, которые могут использовать объект потребовали бы, чтобы они были готовы к тому, чтобы он внезапно стал непригодным [оба условия были бы автоматически выполнены, если бы не было других потоков] ». Yanking ресурс из-под потока является грубым, но если поток заблокирован на вводе/выводе только другие альтернативы, 'Thread.Interrupt()' и 'Thread.Abort()' намного хуже. – supercat

Смежные вопросы