MyThread mt1 = new MyThread("Потомок #1", a);
MyThread mt2 = new MyThread("Потомок #2", a);
mt1.Thrd.Join();
mt2.Thrd.Join();
}
}
В данной программе блокируется вызов метода sa.Sum It(), а не сам метод SumIt(). Ниже приведена соответствующая строка кода, в которой осуществляется подобная блокировка.
// Заблокировать вызовы метода SumIt().
lock(sa) answer = sa.SumIt(a);
Объект sa
является закрытым, и поэтому он может быть благополучно заблокирован. При таком подходе к синхронизации потоков данная программа дает такой же правильный результат, как и при первоначальном подходе.
Ключевое слово lock
на самом деле служит в C# быстрым способом доступа к средствам синхронизации, определенным в классе Monitor
, который находится в пространстве имен System.Threading
. В этом классе определен, в частности, ряд методов для управления синхронизацией. Например, для получения блокировки объекта вызывается метод Enter()
, а для снятия блокировки — метод Exit()
. Ниже приведены общие формы этих методов:
public static void Enter(object obj)
public static void Exit (object obj)
где Enter()
вызывающий поток ожидает до тех пор, пока объект не станет доступным. Тем не менее методы Enter()
и Exit()
применяются редко, поскольку оператор lock
автоматически предоставляет эквивалентные средства синхронизации потоков. Именно поэтому оператор lock
оказывается "более предпочтительным" для получения блокировки объекта при программировании на С#.
Впрочем, один метод из класса Monitor
может все же оказаться полезным. Это метод TryEnter()
, одна из общих форм которого приведена ниже.
public static bool TryEnter(object obj)
Этот метод возвращает логическое значение true
, если вызывающий поток получает блокировку для объекта false
. Но в любом случае вызывающему потоку придется ждать своей очереди. С помощью метода TryEnter()
можно реализовать альтернативный вариант синхронизации потоков, если требуемый объект временно недоступен.
Кроме того, в классе Monitor
определены методы Wait(), Pulse()
и PulseAll()
, которые рассматриваются в следующем разделе.
Сообщение между потоками с помощью методов Wait(), Pulse() и PulseAll()
Рассмотрим следующую ситуацию. Поток T выполняется в кодовом блоке lock
, и ему требуется доступ к ресурсу Wait(), Pulse()
и PulseAll()
.
Методы Wait(), Pulse()
и PulseAll()
определены в классе Monitor
и могут вызываться только из заблокированного фрагмента блока. Они применяются следующим образом. Когда выполнение потока временно заблокировано, он вызывает метод Wait()
. В итоге поток переходит в состояние ожидания, а блокировка с соответствующего объекта снимается, что дает возможность использовать этот объект в другом потоке. В дальнейшем ожидающий поток активизируется, когда другой поток войдет в аналогичное состояние блокировки, и вызывает метод Pulse()
или PulseAll()
. При вызове метода Pulse()
возобновляется выполнение первого потока, ожидающего своей очереди на получение блокировки. А вызов метода PulseAll()
сигнализирует о снятии блокировки всем ожидающим потокам.
Ниже приведены две наиболее часто используемые формы метода Wait()
.
public static bool Wait(object obj)
public static bool Wait(object obj, int миллисекунд_простоя)