AutoResetEvent Reset immediately after Set

…衆ロ難τιáo~ 提交于 2019-12-09 10:43:59

问题


Consider the following pattern:

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();
}

The purpose of this pattern is to allow external consumers to wait for a certain event (e.g. - a message arriving). WaitForNextEvent is not called from within the class.

To give an example that should be familiar, consider System.Diagnostics.Process. It exposes an Exited event, but it also exposes a WaitForExit method, which allows the caller to wait synchronously until the process exits. this is what I am trying to achieve here.

The reason I need signal.Reset() is that if a thread calls WaitForNextEvent after signal.Set() has already been called (or in other words, if .Set was called when no threads were waiting), it returns immediately, as the event has already been previously signaled.

The question

  • Is it guaranteed that a thread calling WaitForNextEvent() will be signaled before signal.Reset() is called? If not, what are other solutions for implementing a WaitFor method?

回答1:


Instead of using AutoResetEvent or ManualResetEvent, use this:

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();
}

Then change your code like so:

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();
}



回答2:


There is no guarantee. This:

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();

won't print "Work Item Executed" on my system. If I add a Thread.Sleep between the Set and the Reset it prints it. Note that this is very processor dependent, so you could have to create tome threads to "fill" the CPUs. Note that on my PC it's reproducible 50% of the times :-)

For the Exited:

readonly object mylock = new object();

then somewhere:

lock (mylock)
{
    // Your code goes here
}

and the 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;
}

Note that the lock construct isn't compatible with async/await (as aren't nearly all the locking primitives of .NET)




回答3:


I would use TaskCompletionSources:

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();
}

By the time that the code calls SetResult, no new code entering WaitForNextEvent can obtain the TaskCompletionSource that is being signalled.




回答4:


I believe it is not guaranteed.

However, your logic flow is not understood by me. If your main thread Sets the signal, why should it wait until that signal reaches its destination? Wouldn't it be better to continue your "after signal set" logic in that thread which was waiting?

If you cannot do that, I recommend you to use second WaitHandle to signal the first thread that the second one has reveiced the signal. But I cannot see any pros of such a strategy.



来源:https://stackoverflow.com/questions/18609285/autoresetevent-reset-immediately-after-set

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!