return;
}
Console.Write("так ") ;
}
}
}
После этой подстановки результат выполнения данной программы будет выглядеть следующим образом.
тик так так так так так тик тик тик тик Часы остановлены
Очевидно, что методы Tick() и Tock() больше не синхронизированы!
Взаимоблокировка и состояние гонки
При разработке многопоточных программ следует быть особенно внимательным, чтобы избежать взаимоблокировки и состояний гонок.
На первый взгляд избежать взаимоблокировки нетрудно, но на самом деле не все так просто, ведь взаимоблокировка может возникать окольными путями. В качестве примера рассмотрим класс TickTock из предыдущей программы. Как пояснялось выше, в отсутствие завершающего вызова метода Pulse() из метода Tick() или Tock() тот или другой будет ожидать до бесконечности, что приведет к "зависанию" программы вследствие взаимоблокировки. Зачастую причину взаимоблокировки не так-то просто выяснить, анализируя исходный код программы, поскольку параллельно действующие процессы могут взаимодействовать довольно сложным образом во время выполнения.
Применение атрибута MethodlmplAttribute
Метод может быть полностью синхронизирован с помощью атрибута MethodlmplAttribute. Такой подход может стать альтернативой оператору lock в тех случаях, когда метод требуется заблокировать полностью. Атрибут MethodlmplAttгibute определен в пространстве имен System.Runtime.CompilerServices. Ниже приведен конструктор, применяемый для подобной синхронизации:
public MethodlmplAttribute(MethodlmplOptions methodlmplOptions)
где MethodlmplOptions.Synchronized. Этот атрибут вызывает блокировку всего метода для текущего экземпляра объекта, доступного по ссылке this. Если же метод относится к типу static, то блокируется его тип. Поэтому данный атрибут непригоден для применения в открытых объектах или классах.
Ниже приведена еще одна версия программы, имитирующей тиканье часов, с переделанным вариантом класса TickTock, в котором атрибут MethodlmplOptions обеспечивает должную синхронизацию.
// Использовать атрибут MethodlmplAttribute
// для синхронизации метода.
using System;
using System.Threading;
using System.Runtime.CompilerServices;
//Вариант класса TickTock, переделанный с целью
// использовать атрибут MethodlmplOptions.Synchronized,
class TickTock {
/* Следующий атрибут полностью синхронизирует метод Tick(). */
[MethodImplAttribute(MethodImplOptions.Synchronized)]
public void Tick(bool running) {
if (!running) { // остановить часы
Monitor.Pulse(this); // уведомить любые ожидающие потоки
return;
}