Когда используется несколько потоков, то иногда приходится координировать действия двух или более потоков. Процесс достижения такой координации называется
В основу синхронизации положено понятие
Средство блокировки встроено в язык С#. Благодаря этому все объекты могут быть синхронизированы. Синхронизация организуется с помощью ключевого слова lock
. Она была предусмотрена в C# с самого начала, и поэтому пользоваться ею намного проще, чем кажется на первый взгляд. В действительности синхронизация объектов во многих программах на C# происходит практически незаметно.
Ниже приведена общая форма блокировки:
lock(lockObj) {
// синхронизируемые операторы
}
где lock
гарантирует, что фрагмент кода, защищенный блокировкой для данного объекта, будет использоваться только в потоке, получающем эту блокировку. А все остальные потоки блокируются до тех пор, пока блокировка не будет снята. Блокировка снимается по завершении защищаемого ею фрагмента кода.
Блокируемым считается такой объект, который представляет синхронизируемый ресурс. В некоторых случаях им оказывается экземпляр самого ресурса или же произвольный экземпляр объекта, используемого для синхронизации. Следует, однако, иметь в виду, что блокируемый объект не должен быть общедоступным, так как в противном случае он может быть заблокирован из другого, неконтролируемого в программе фрагмента кода и в дальнейшем вообще не разблокируется. В прошлом для блокировки объектов очень часто применялась конструкция lock(this)
. Но она пригодна только в том случае, если this
является ссылкой на закрытый объект. В связи с возможными программными и концептуальными ошибками, к которым может привести конструкция lock(this)
, применять ее больше не рекомендуется. Вместо нее лучше создать закрытый объект, чтобы затем заблокировать его. Именно такой подход принят в примерах программ, приведенных далее в этой главе. Но в унаследованном коде C# могут быть обнаружены примеры применения конструкции lock(this)
. В одних случаях такой код оказывается безопасным, а в других — требует изменений во избежание серьезных осложнений при его выполнении.
В приведенной ниже программе синхронизация демонстрируется на примере управления доступом к методу SumIt()
, суммирующему элементы целочисленного массива.
// Использовать блокировку для синхронизации доступа к объекту.
using System;
using System.Threading;
class SumArray {
int sum;
object lockOn = new object(); // закрытый объект, доступный
// для последующей блокировки
public int SumIt(int[] nums) {
lock(lockOn) { // заблокировать весь метод
sum =0; // установить исходное значение суммы
for(int i=0; i < nums.Length; i++) {
sum += nums[i];
Console.WriteLine("Текущая сумма для потока " +
Thread.CurrentThread.Name + " равна " +
sum);
Thread.Sleep(10); // разрешить переключение задач
}
return sum;
}
}
}
class MyThread {
public Thread Thrd;
int[] a;
int answer;
// Создать один объект типа SumArray для всех
// экземпляров класса MyThread.
static SumArray sa = new SumArray();
// Сконструировать новый поток,