Впрочем, один метод из класса Monitor
может все же оказаться полезным. Это метод TryEnter()
, одна из общих форм которого приведена ниже.
public static bool TryEnter(object obj)
Этот метод возвращает логическое значение true
, если вызывающий поток получает блокировку для объекта obj, а иначе он возвращает логическое значение false
. Но в любом случае вызывающему потоку придется ждать своей очереди. С помощью метода TryEnter()
можно реализовать альтернативный вариант синхронизации потоков, если требуемый объект временно недоступен.
Кроме того, в классе Monitor
определены методы Wait(), Pulse()
и PulseAll()
, которые рассматриваются в следующем разделе.
Сообщение между потоками с помощью методов Wait(), Pulse() и PulseAll()
Рассмотрим следующую ситуацию. Поток T выполняется в кодовом блоке lock
, и ему требуется доступ к ресурсу R, который временно недоступен. Что же тогда делать потоку T? Если поток T войдет в организованный в той или иной форме цикл опроса, ожидая освобождения ресурса R, то тем самым он свяжет соответствующий объект, блокируя доступ к нему других потоков. Это далеко не самое оптимальное решение, поскольку оно лишает отчасти преимуществ программирования для многопоточной среды. Более совершенное решение заключается в том, чтобы временно освободить объект и тем самым дать возможность выполняться другим потокам. Такой подход основывается на некоторой форме сообщения между потоками, благодаря которому один поток может уведомлять другой о том, что он заблокирован и что другой поток может возобновить свое выполнение. Сообщение между потоками организуется в C# с помощью методов 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 миллисекунд_простоя)
В первой форме ожидание длится вплоть до уведомления об освобождении объекта, а во второй форме — как до уведомления об освобождении объекта, так и до истечения периода времени, на который указывает количество миллисекунд_простоя. В обеих формах obj обозначает объект, освобождение которого ожидается.
Ниже приведены общие формы методов Pulse()
и PulseAll()
:
public static void Pulse(object obj)
public static void PulseAll(object obj)
где obj обозначает освобождаемый объект.
Если методы Wait(),Pulse()
и PulseAll()
вызываются из кода, находящегося за пределами синхронизированного кода, например из блока lock
, то генерируется исключение SynchronizationLockException
.
Пример использования методов Wait() и Pulse()
Для того чтобы стало понятнее назначение методов Wait()
и Pulse()
, рассмотрим пример программы, имитирующей тиканье часов и отображающей этот процесс на экране словами "тик" и "так". Для этой цели в программе создается класс TickTock
, содержащий два следующих метода: Tick()
и Тоск()
. Метод Tick()
выводит на экран слово "тик", а метод Тоск()
— слово "так". Для запуска часов далее в программе создаются два потока: один из них вызывает метод Tick()
, а другой — метод Тоск()
. Преследуемая в данном случае цель состоит в том, чтобы оба потока выполнялись, поочередно выводя на экран слова "тик" и "так", из которых образуется повторяющийся ряд "тик-так", имитирующий ход часов.
//Использовать методы Wait() и Pulse() для иммитации
//тиканья часов
using System;
using System.Threading;
class TickTock {
object lockOn = new object();
public void Tick(bool running) {
Читать дальше