Безопасность потоков .NET - PullRequest
       14

Безопасность потоков .NET

8 голосов
/ 23 августа 2010

List.Add является членом экземпляра.Это означает, что он не гарантированно безопасен для потоков.Что это значит?

Возможность 1. Что, если два потока вызывают. Добавить в разных случаях, может быть неожиданный результат в зависимости от фазы Луны?вызов двух потоков. Добавление одного и того же экземпляра может привести к неожиданному результату в зависимости от фазы луны, и если экземпляры разные, потенциальной проблемы не будет.использовать потоки вообще, поэтому они написали .NET, чтобы быть неоднозначным.

Ответы [ 5 ]

10 голосов
/ 23 августа 2010

Возможность 1 не соответствует действительности. Для метода экземпляра было бы настолько необычно вызывать проблемы для других экземпляров, что это было бы четко задокументировано (не только с помощью указания, указывающего на это, но и с некоторым обоснованием, поскольку это, как правило, является признаком очень плохого кодирования, поэтому, если на это была веская причина, это будет указано).

Возможность 3 не соответствует действительности, поскольку они только что задокументировали поведение потоков.

Возможность 2 частично имеет место. Тем не менее, взаимодействие также может происходить с одним потоком, вызывающим Add, а другой - с другим методом, не поддерживающим поток.

Большинство изменяемых классов, предоставляемых платформой, являются поточно-безопасными для статических членов и не поточно-безопасными для методов экземпляра. Это по уважительной причине.

  1. Если статический метод не является поточно-ориентированным, очень трудно выполнять вызовы этого метода в поточно-ориентированном режиме, особенно если класс может использоваться разными уровнями кода, написанными разными людьми. Это делает усилия, направленные на то, чтобы сделать такие методы потокобезопасными, почти всегда оправданными. Большинство таких элементов также относительно легко сделать потокобезопасными в любом случае (если избегать наличия изменяемого статического состояния, что всегда полезно избегать).

  2. Значительное использование отдельных объектов будет осуществляться одним потоком за раз, без возможности доступа к нему другого потока. Это делает трудным обеспечение правильности, с риском тупика, если он пойдет не так, и накладные расходы, накладываемые на производительность, трудно оправдать. Для человека, использующего класс, относительно также легко гарантировать, что экземпляр, который используется несколькими потоками, используется в поточно-безопасном режиме.

Там делается большой акцент на «относительно», поскольку написание многопоточного кода не всегда легко. Иногда это довольно просто (неизменяемые классы занимают немного времени, чтобы сделать не поточнобезопасным!), Но чаще это очень сложно (отсюда много вопросов по этой теме здесь и в других местах).

И все же именно поэтому бремя должно быть наложено на пользователя в таких случаях. Сделать такой класс полностью потокобезопасным настолько сложно (а иногда и доказуемо невозможно), что результаты будут неприемлемы для большинства пользователей, которые являются людьми, которые в лучшем положении могут судить о том, какая защита необходима в данном случае.

4 голосов
/ 23 августа 2010

Я думаю, List.Add плохой пример. List.Remove намного лучше, потому что на самом деле есть интересные проблемы с многопоточностью. Один поток может попытаться получить доступ к элементу, тогда как другой поток попытается вызвать List.Remove для него. Теперь может случиться так, что элемент будет удален при попытке получить к нему доступ, что приведет к NullReferenceException. Тем не менее, в основном это отказ от ответственности, чтобы знать об этом, так как нет механизмов блокировки на месте. Просто помните lock всякий раз, когда два потока могут пытаться получить доступ к одному и тому же объекту или коду кода, чтобы предотвратить такие проблемы.

0 голосов
/ 23 августа 2010

Если два разных потока изменяют один и тот же список без синхронизации / блокировки, это может вызвать проблемы. Два потока, работающих с разными списками, будут в порядке. То же самое относится и к большинству классов - на самом деле очень и очень мало классов явно заявляют, что «этот класс является потокобезопасным», но почти все они безопасны, если вы не разделяете (обращаетесь к) экземпляры между потоками. Если класс ломается даже тогда, когда потоки не делятся экземплярами, документы скажут об этом - но это такая уродливая ситуация, что я надеюсь, что MS не допустит этого в API.

Microsoft говорит и делает то, что делает (в отношении безопасности потоков) по одной огромной причине:

Потокобезопасность трудна.

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

0 голосов
/ 23 августа 2010

Если два потока одновременно пытаются сделать разные вещи для одного и того же экземпляра списка, могут произойти плохие вещи. Единственная ситуация, когда несколько потоков могут одновременно выполнять разные действия со списком, - это когда все потоки читают. Я думаю, что это безопасно для списка, хотя даже это не обязательно может быть безопасно для всех контейнерных классов.

Коллекции, подобные спискам в .net, обычно хранятся в массивах; если коллекция становится слишком большой для своего массива, будет выделен больший массив. Если несколько потоков имеют дело с одной и той же коллекцией и не имеют каких-либо блокировок, один поток мог бы добавить что-то, что увеличило бы список, а затем другие потоки попытались бы изменить список между тем временем, когда скопировать и время начала коллекции с использованием нового списка. Это может привести к тому, что изменения будут потеряны или могут произойти другие плохие вещи.

0 голосов
/ 23 августа 2010

Поскольку механизм блокировки не реализован в элементах экземпляра, они поместили этот отказ на веб-сайте MSDN.

См. Также Статика и безопасность резьбы

и Статика и безопасность резьбы: часть II

...