заблокировать ключевое слово в C # - PullRequest
43 голосов
/ 12 сентября 2008

Я понимаю основную функцию ключевого слова блокировки от MSDN

Оператор блокировки (C # Reference)

Ключевое слово lock отмечает оператор блок как критический раздел получение блокировки взаимоисключения для данного объекта, выполняя заявление, а затем выпустив блокировка.

Когда следует использовать замок?

Например, это имеет смысл с многопоточными приложениями, поскольку защищает данные. Но нужно ли это, когда приложение не раскручивает другие потоки?

Есть ли проблемы с производительностью при использовании блокировки?

Я только что унаследовал приложение, которое повсеместно использует блокировку, и оно однопоточное, и я хочу знать, если я оставлю их, нужны ли они вообще?

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

Ответы [ 10 ]

58 голосов
/ 12 сентября 2008

Когда следует использовать замок?

Блокировка должна использоваться для защиты общих ресурсов в многопоточном коде. Ни для чего другого.

Но нужно ли это, когда приложение не раскручивает другие потоки?

Абсолютно нет. Это просто трата времени. Однако убедитесь, что вы не используете системные потоки. Например, если вы используете асинхронный ввод-вывод, вы можете получать обратные вызовы из случайного потока, а не из исходного потока.

Есть ли проблемы с производительностью при использовании блокировки?

Да. Они не очень большие в однопоточном приложении, но зачем делать звонки, которые вам не нужны?

... если это хороший шаблон проектирования, которому нужно следовать в будущем [?]

Запирать все волей-неволей - ужасный шаблон дизайна. Если ваш код перегружен случайной блокировкой, и вы решили использовать фоновый поток для какой-то работы, вы, скорее всего, столкнетесь с тупиками. Совместное использование ресурса между несколькими потоками требует тщательного проектирования, и чем больше вы можете выделить сложную часть, тем лучше.

7 голосов
/ 13 сентября 2008

Все ответы здесь кажутся правильными: полезность блокировок состоит в том, чтобы блокировать потоки от одновременного доступа к заблокированному коду. Однако в этом поле есть много тонкостей, одна из которых заключается в том, что заблокированные блоки кода автоматически помечаются как критические области средой выполнения Common Language.

Эффект кода, помеченного как критический, заключается в том, что, если весь регион не может быть полностью выполнен, среда выполнения может посчитать, что весь ваш домен приложения потенциально находится под угрозой, и, следовательно, выгрузить его из памяти. Цитировать MSDN :

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

Поэтому, даже если ваше приложение является однопоточным, это может представлять опасность для вас. Учтите, что один метод в заблокированном блоке генерирует исключение, которое в конечном итоге не обрабатывается в блоке. Даже если исключение обработано, поскольку оно всплывает через стек вызовов, ваша критическая область кода не завершилась нормально. А кто знает, как отреагирует CLR?

Для получения дополнительной информации прочитайте эту статью об опасностях Thread.Abort () .

6 голосов
/ 12 сентября 2008

Помните, что могут быть причины, по которым ваше приложение не так однопоточно, как вы думаете. Например, асинхронный ввод-вывод в .NET вполне может выполнять обратный вызов в потоке пула, как и некоторые из различных классов таймеров (но не в Windows Forms Timer).

2 голосов
/ 12 сентября 2008
Блокировка

должна использоваться вокруг кода, который изменяет совместно используемое состояние, состояние, которое изменяется другими потоками одновременно, и эти другие шаги должны иметь такую ​​же блокировку.

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

Чтобы ответить на ваш вопрос, блокировка не требуется в однопоточном приложении, и она имеет побочные эффекты производительности. потому что блокировки в C # основаны на объектах синхронизации ядра, и каждая ваша блокировка создает переход в режим ядра из пользовательского режима.

Если вы заинтересованы в производительности многопоточности, лучше всего начать с Рекомендации по созданию потоков MSDN

2 голосов
/ 12 сентября 2008

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

1 голос
/ 13 сентября 2008

См. вопрос о 'Mutex' в C #. А затем посмотрите на эти два вопроса, касающихся конкретно использования оператора 'lock (Object)'.

1 голос
/ 12 сентября 2008

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

И блокировка вызывает снижение производительности, добавляя инструкции для проверки одновременного доступа перед выполнением кода. Он должен использоваться только при необходимости.

1 голос
/ 12 сентября 2008

Да, при использовании блокировки будет некоторое снижение производительности, но оно, как правило, достаточно незначительное, чтобы не иметь значения.

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

1 голос
/ 12 сентября 2008

Вы можете иметь проблемы с производительностью блокировки переменных, но обычно вы создаете свой код, чтобы минимизировать отрезки времени, которые тратятся внутри «заблокированного» блока кода.

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

0 голосов
/ 12 сентября 2008

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

...