|
Вопрос по Курсу: "Параллельное программирование с использованием MS VisualStudia 2010". При компиляции Самостоятельного задания (одновременная отрисовка прямоугольников, эллипсов и выдача в текст-бокс случайного числа) среда предупреждает: suspend - устаревшая команда; примените monitor, mutex и т.п. Создаётся впечатление, что Задание создано в более поздней среде, чем VS 2010. |
Синхронизация потоков
Методы класса Monitor: Wait, Pulse и PulseAll
Методы Wait(), Pulse() и PulseAll() определены в классе Monitor и могут вызываться только из заблокированного фрагмента блока (lock - оператор). Когда выполнение потока временно блокируется, вызывается метод Wait(), т.е. он переходит в режим ожидания и снимает блокировку с объекта, позволяя другому потоку использовать этот объект. Позже, когда другой поток входит в аналогичное состояние блокирования и вызывает метод Pulse() или PulseAll(), "спящий" поток "просыпается". Обращение к методу Pulse() возобновляет выполнение потока, стоящего первым в очереди потоков, пребывающих в режиме ожидания. Обращение к методу PulseAll() сообщает о снятии блокировки всем ожидающим потокам.
Два наиболее используемых формата использования метода Wait():
public static bool Wait(object waitOb) public static bool Wait(object waitOb, int миллисекунд_простоя)
Первый формат означает ожидание до уведомления. Второй - ожидает до уведомления или до истечения периода времени, заданного в миллисекундах. В обоих случаях параметр waitOb задает объект, к которому ожидается доступ. Форматы использования методов Pulse() и PulseAll() приведены ниже:
public static void Pulse(object waitOb) public static void PulseAll(object waitOb)
где параметр waitOb - означает объект, освобождаемый от блокировки.
Если метод Wait(), Pulse() или PulseAll() вызывается из кода, который находится вне lock блока, генерируется исключение типа SynchronizationLockException.
В качестве примера работы методов Wait() и Pulse(), создадим программу, которая имитирует работу часов посредством отображения на экране слов "тик" и "так". Для этого создадим класс TickTock, который содержит два метода: tick() - отображает слово "тик" и tock() - отображает слово "так":
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace TickTack
{
class TickTock
{
public void tick(bool running)
{
lock (this)
{
if (!running) { // Остановка часов.
Monitor.Pulse(this); // Уведомление любых
// ожидающих потоков.
return;
}
Console.Write("тик ");
Monitor.Pulse(this); // Разрешает выполнение
// метода tock().
Monitor.Wait(this); // Ожидаем завершения
// метода tock().
}
}
public void tock(bool running)
{
lock (this)
{
if (!running) { // Остановка часов.
Monitor.Pulse(this); // Уведомление любых
// ожидающих потоков.
return;
}
Console.WriteLine("так");
Monitor.Pulse(this); // Разрешает выполнение
// метода tick().
Monitor.Wait(this); // Ожидаем завершения
// метода tick().
}
}
}
class MyThread
{
public Thread thrd;
TickTock ttOb;
// Создаем новый поток.
public MyThread(string name, TickTock tt)
{
thrd = new Thread(new ThreadStart(this.run));
ttOb = tt;
thrd.Name = name;
thrd.Start();
}
// Начинаем выполнение нового потока.
void run()
{
if (thrd.Name == "тик")
{
for (int i = 0; i < 5; i++) ttOb.tick(true);
ttOb.tick(false);
}
else
{
for (int i = 0; i < 5; i++) ttOb.tock(true);
ttOb.tock(false);
}
}
}
class Program
{
static void Main(string[] args)
{
TickTock tt = new TickTock();
MyThread mt1 = new MyThread("тик", tt);
MyThread mt2 = new MyThread("так", tt);
mt1.thrd.Join();
mt2.thrd.Join();
Console.WriteLine("Часы остановлены");
Console.ReadLine();
}
}
}При выполнении программа сгенерирует результаты, представленные на Рис. 5.3.
увеличить изображение
Рис. 5.3. Результат выполнения программы с использованием методов Wait() и Pulse()
В методе Main() создается объект класса TickTock - tt, который используется для запуска двух потоков на выполнение. Если в методе Run() из класса MyThread обнаруживается имя потока mt1, соответствующее ходу часов "тик", то вызывается метод tick(). А если это имя потока mt2, соответствующее ходу часов "так", то вызывается метод tock().
Каждый из их методов вызывается пять раз подряд с передачей логического значения true в качестве аргумента. Часы идут до тех пор, пока этим методам передается логическое значение true, и останавливаются, как только передается логическое значение false:
public void tick(bool running)
{
lock (this)
{
if (!running) { // Остановка часов.
Monitor.Pulse(this); // Уведомление любых
// ожидающих потоков.
return;
}
Console.Write("тик ");
Monitor.Pulse(this); // Разрешает выполнение
// метода tock().
Monitor.Wait(this); // Ожидаем завершения
// метода tock().
}
}Методы Wait() и Pulse() могут использоваться только в синхронизированных блоках кода. Вначале метода tick() проверяется значение текущего параметра, которое служит явным признаком остановки часов. Если это логическое значение false, то часы остановлены. В этом случае вызывается метод Pulse(), разрешающий выполнение любого потока, ожидающего своей очереди.
Если же часы идут при выполнении метода tick(), то на экран выводится слово "тик" с пробелом, затем вызывается метод Pulse(), а после него - метод Wait(). При вызове метода Pulse() разрешается выполнение потока для того же самого объекта, а при вызове метода Wait() выполнение метода tock() приостанавливается до тех пор, пока метод Pulse() не будет вызван из другого потока. Теперь, уберем методы Wait() и Pulse() из созданной ранее программы:
class TickTock
{
public void tick(bool running)
{
lock (this)
{
if (!running)
{ // Остановка часов.
return;
}
Console.Write("тик ");
}
}
public void tock(bool running)
{
lock (this)
{
if (!running)
{ // Остановка часов.
return;
}
Console.WriteLine("так");
}
}
}При выполнении программа сгенерирует результаты, представленные на Рис. 5.4.
увеличить изображение
Рис. 5.4. Результат выполнения программы без использования методов Wait() и Pulse()
Как видно из результатов выполнения программы, методы tick() и tock() больше не синхронизированы.

