Потомок #1 начат.
Потомок #2 начат.
Текущая сумма для потока Потомок #1 равна 1
Текущая сумма для потока Потомок #2 равна 1
Текущая сумма для потока Потомок #1 равна 3
Текущая сумма для потока Потомок #2 равна 5
Текущая сумма для потока Потомок #2 равна 11
Текущая сумма для потока Потомок #1 равна 8
Текущая сумма для потока Потомок #1 равна 15
Текущая сумма для потока Потомок #2 равна 19
Текущая сумма для потока Потомок #1 равна 24
Текущая сумма для потока Потомок #2 равна 29
Сумма для потока Потомок #1 равна 29
Потомок #1 завершен.
Сумма для потока Потомок #2 равна 29
Потомок #2 завершен.
Как следует из приведенного выше результата, в обоих порожденных потоках метод SumIt() используется одновременно для одного и того же объекта, а это приводит к искажению значения в поле sum.
Ниже подведены краткие итоги использования блокировки.
• Если блокировка любого заданного объекта получена в одном потоке, то после блокировки объекта она не может быть получена в другом потоке.
• Остальным потокам, пытающимся получить блокировку того же самого объекта, придется ждать до тех пор, пока объект не окажется в разблокированном состоянии.
• Когда поток выходит из заблокированного фрагмента кода, соответствующий объект разблокируется.
Несмотря на всю простоту и эффективность блокировки кода метода, как показано в приведенном выше примере, такое средство синхронизации оказывается пригодным далеко не всегда. Допустим, что требуется синхронизировать доступ к методу класса, который был создан кем-то другим и сам не синхронизирован. Подобная ситуация вполне возможна при использовании чужого класса, исходный код которого недоступен. В этом случае оператор lock
нельзя ввести в соответствующий метод чужого класса. Как же тогда синхронизировать объект такого класса? К счастью, этот вопрос разрешается довольно просто: доступ к объекту может быть заблокирован из внешнего кода по отношению к данному объекту, для чего достаточно указать этот объект в операторе lock
. В качестве примера ниже приведен другой вариант реализации предыдущей программы. Обратите внимание на то, что код в методе SumIt()
уже не является заблокированным, а объект lockOn
больше не объявляется. Вместо этого вызовы метода SumIt()
блокируются в классе MyThread
.
// Другой способ блокировки для синхронизации доступа к объекту,
using System;
using System.Threading;
class SumArray {
int sum;
public int SumIt(int[] nums) {
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();
// Сконструировать новый поток,
public MyThread(string name, int[] nums) {
a = nums;
Thrd = new Thread(this.Run);
Thrd.Name = name;
Thrd.Start(); // начать поток
}
// Начать выполнение нового потока,
void Run() {
Console.WriteLine(Thrd.Name + " начат.");
// Заблокировать вызовы метода SumIt().
lock(sa) answer = sa.SumIt(a);
Console.WriteLine("Сумма для потока " + Thrd.Name +
" равна " + answer);
Console.WriteLine(Thrd.Name + " завершен.");
}
}
class Sync {
static void Main() {
int[] a = {1, 2, 3, 4, 5};