Ключевое слово lock используется в языке C# для обозначения так называемой исключающей блокировки, используется при работе с потоками (thread). Это один из способов решения (имхо – самый верный) решения проблемы чтения-записи данных разными потоками.

Рассмотрим на примере кода небольшой программы (Листинг 1). Есть коллекция типа Int, содержащая 4 элемента. Есть 2 потока – один (th1) читает данные из коллекции, другой (th2) – добавляет новое значение в коллекцию.

Состояния потоков для Листинга 1:

  1. Потоки в состоянии покоя
  2. Поток-читатель запускается с задержкой в 1000 мс, на каждой итерации цикла также происходит задержка на 1000 мс.
  3. Поток-писатель запускается с задержкой 1200мс.

 

Листинг 1

 

Выполнение данной программы начнется с вывода надписей "Item 5" и "Добавлена запись в коллекцию, но следом будет сообщение об ошибке: "Необработанное исключение: System.InvalidOperationException: Коллекция была изменена; невозможно выполнить операцию перечисления.".

 

Рис.1. Ошибка изменения коллекции

Рис.1. Ошибка изменения коллекции

 

Поток-писатель изменил коллекцию, которую в это же время читал поток-читатель, что и привело к ошибке.

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

С оператором lock указывается параметр, на который распространяется область действия блокировки. Модифицируем код Листинга 1 так, чтобы на работу с коллекциями были наложены блокировки (Листинг 2)

 

Листинг 2

 

В листинге 2 добавлены 2 исключающие блокировки для коллекции "elements". При таком коде поток-писатель будет ждать права внесения изменения в коллекцию до тех пор, пока не отработает поток-читатель, т.е. в данном примере потоки выполнятся последовательно.