Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Многопоточность.doc
Скачиваний:
4
Добавлен:
28.10.2018
Размер:
225.79 Кб
Скачать

Класс ManualResetEvent

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

Таким образом вам решать, когда использовать каждый из этих классов.

Блокировка потоков

При реализации синхронизации работы потоков следует соблюдать осторожность. Вполне возможны ситуации, когда потоки будут ждать друг друга и никогда не освободятся. Такие ситуации называются взаимной блокировкой. Рассмотрим следующий пример.

Пусть для каких-то целей нам нужно очень быстро вычислять значение какой-либо функции. Стандартным приемом в этом случае является табуляция. Значения функции в нужных точках вычисляются и, затем, сохраняются в кэше. Метод GetValue, выполняясь в отдельном потоке, извлекает необходимые ему значения функции из кэша. Если же он не нашел значения в кэше, он помещает нужый ему аргумент функции в отдельный список. Другой метод (CalcFunction), выполняющийся так же в отдельном потоке, извлекает аргументы функции из этого списка, вычисляет значение функции и помещает ее в кэш.

Поскольку и список аргументов и кэш могут быть изменены в любой момент, то оба метода используют оператор lock для синхронизации доступа к этим объектам (программа ThreadingBlocking):

private static void GetValue(object state)

{

Random rnd = new Random();

while (true)

{

int number = rnd.Next(1000);

lock (squares)

{

if (squares.ContainsKey(number))

{

Console.WriteLine("Square of {0} is {1}.", number, squares[number]);

continue;

}

lock (numbersToProcess)

{

numbersToProcess.Add(number);

}

}

}

}

private static void CalcFunction(object state)

{

while (true)

{

lock (numbersToProcess)

{

if (numbersToProcess.Count > 0)

{

int number = numbersToProcess[0];

numbersToProcess.Remove(0);

lock (squares)

{

squares[number] = number * number;

}

Console.WriteLine("Square for {0} is calculated.", number);

}

}

}

}

Как видно из кода, метод GetValue сперва захватывает кэш, а потом список аргументов, метод же CalcFunction наоборот сперва захватывает список аргументов, а затем кэш. Именно поэтому оба потока взаимно блокируют друг друга. Первый успевает захватить кэш, а второй – список аргументов. Затем они будут ждать друг друга, пытаясь захватить уже заблокированные другим потоком объекты.

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