AutoResetEvent Сброс сразу после установки
Рассмотрим следующую схему:
private AutoResetEvent signal = new AutoResetEvent(false);
private void Work()
{
while (true)
{
Thread.Sleep(5000);
signal.Set();
//has a waiting thread definitely been signaled by now?
signal.Reset();
}
}
public void WaitForNextEvent()
{
signal.WaitOne();
}
Цель этого шаблона - позволить внешним потребителям ожидать определенного события (например, прибытие сообщения). WaitForNextEvent
не вызывается из класса.
Чтобы привести пример, который должен быть знаком, рассмотрим System.Diagnostics.Process
, Это подвергает Exited
событие, но оно также подвергает WaitForExit
метод, который позволяет вызывающей стороне синхронно ожидать завершения процесса. это то, что я пытаюсь достичь здесь.
Причина мне нужна signal.Reset()
это если поток вызывает WaitForNextEvent
после signal.Set()
уже был вызван (или, другими словами, если .Set
был вызван, когда ни один поток не ожидал), он немедленно возвращается, так как событие уже было сигнализировано ранее.
Вопрос
- Гарантируется ли, что поток вызывает
WaitForNextEvent()
будет сигнализироваться раньшеsignal.Reset()
называется? Если нет, то каковы другие решения для реализацииWaitFor
метод?
4 ответа
Вместо того, чтобы использовать AutoResetEvent
или же ManualResetEvent
, использовать этот:
public sealed class Signaller
{
public void PulseAll()
{
lock (_lock)
{
Monitor.PulseAll(_lock);
}
}
public void Pulse()
{
lock (_lock)
{
Monitor.Pulse(_lock);
}
}
public void Wait()
{
Wait(Timeout.Infinite);
}
public bool Wait(int timeoutMilliseconds)
{
lock (_lock)
{
return Monitor.Wait(_lock, timeoutMilliseconds);
}
}
private readonly object _lock = new object();
}
Затем измените ваш код следующим образом:
private Signaller signal = new Signaller();
private void Work()
{
while (true)
{
Thread.Sleep(5000);
signal.Pulse(); // Or signal.PulseAll() to signal ALL waiting threads.
}
}
public void WaitForNextEvent()
{
signal.Wait();
}
Там нет гарантии. Это:
AutoResetEvent flag = new AutoResetEvent(false);
new Thread(() =>
{
Thread.CurrentThread.Priority = ThreadPriority.Lowest;
Console.WriteLine("Work Item Started");
flag.WaitOne();
Console.WriteLine("Work Item Executed");
}).Start();
// For fast systems, you can help by occupying processors.
for (int ix = 0; ix < 2; ++ix)
{
new Thread(() => { while (true) ; }).Start();
}
Thread.Sleep(1000);
Console.WriteLine("Sleeped");
flag.Set();
// Decomment here to make it work
//Thread.Sleep(1000);
flag.Reset();
Console.WriteLine("Finished");
Console.ReadLine();
не будет печатать "Задание выполнено" в моей системе. Если я добавлю Thread.Sleep
между Set
и Reset
это печатает это. Обратите внимание, что это сильно зависит от процессора, поэтому вам может потребоваться создать потоки тома, чтобы "заполнить" процессоры. Обратите внимание, что на моем ПК это воспроизводимо в 50% случаев:-)
Для вышедших:
readonly object mylock = new object();
тогда где-то:
lock (mylock)
{
// Your code goes here
}
и WaitForExit
:
void WaitForExit()
{
lock (mylock) ;
// exited
}
void bool IsExited()
{
bool lockTacken = false;
try
{
Monitor.TryEnter(mylock, ref lockTacken);
}
finally
{
if (lockTacken)
{
Monitor.Exit(mylock);
}
}
return lockTacken;
}
Обратите внимание, что lock
конструкция не совместима с async
/await
(как и не все блокирующие примитивы.NET)
Я хотел бы использовать TaskCompletionSource
s:
private volatile TaskCompletionSource<int> signal = new TaskCompletionSource<int>();
private void Work()
{
while (true)
{
Thread.Sleep(5000);
var oldSignal = signal;
signal = new TaskCompletionSource<int>()
//has a waiting thread definitely been signaled by now?
oldSignal.SetResult(0);
}
}
public void WaitForNextEvent()
{
signal.Task.Wait();
}
К тому времени, когда код вызывает SetResult
нет ввода нового кода WaitForNextEvent
может получить TaskCompletionSource
это сигнализируется.
Я считаю, что это не гарантировано.
Однако твой логический поток не понят мной. Если ваша основная тема Set
s сигнал, почему он должен ждать, пока этот сигнал достигнет пункта назначения? Не лучше ли продолжить вашу логику "после набора сигналов" в том потоке, который ожидал?
Если вы не можете сделать это, я рекомендую вам использовать второй WaitHandle
сигнализировать первому потоку, что второй обнаружил сигнал. Но я не вижу никаких плюсов такой стратегии.