object lockOn = new object ();
public void Tick(bool running) { lock(lockOn) {
if (!running) { // остановить часы return;
}
Console.Write('тик ') ;
}
}
public void Tock (bool running) { lock(lockOn) {
if(!running) { // остановить часы return;
}
После этой подстановки результат выполнения данной программы будет выглядеть следующим образом.
тик тик тик тик тик так
так
так
так
так
Часы остановлены
Очевидно, что методы Tick () и Tock () больше не синхронизированы!
Взаимоблокировка и состояние гонки
При разработке многопоточных программ следует быть особенно внимательным, чтобы избежать взаимоблокировки и состояний гонок.
На первый взгляд избежать взаимоблокировки нетрудно, но на самом деле не все так просто, ведь взаимоблокировка может возникать окольными путями. В качестве примера рассмотрим класс TickTock из предыдущей программы. Как пояснялось выше, в отсутствие завершающего вызова метода Pulse () из метода Tick () или Tock () тот или другой будет ожидать до бесконечности, что приведет к 'зависанию' программы вследствие взаимоблокировки. Зачастую причину взаимоблокировки не так-то просто выяснить, анализируя исходный код программы, поскольку параллельно действующие процессы могут взаимодействовать довольно сложным образом во время выполнения.
Применение атрибута MethodlmplAttribute
Метод может быть полностью синхронизирован с помощью атрибута MethodlmplAttribute. Такой подход может стать альтернативой оператору lock в тех случаях, когда метод требуется заблокировать полностью. Атрибут
MethodlmplAttгibute определен в пространстве имен System . Runtime . CompilerServices. Ниже приведен конструктор, применяемый для подобной синхронизации:
public MethodlmplAttribute(MethodlmplOptions
где
Ниже приведена еще одна версия программы, имитирующей тиканье часов, с переделанным вариантом класса TickTock, в котором атрибут MethodlmplOptions обеспечивает должную синхронизацию.
// Использовать атрибут MethodlmplAttribute для синхронизации метода.
