简体   繁体   English

AutoResetEvent 设置后立即重置

[英]AutoResetEvent Reset immediately after Set

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 (eg - a message arriving).此模式的目的是允许外部消费者等待某个事件(例如 - 消息到达)。 WaitForNextEvent is not called from within the class. WaitForNextEvent不是从类内部调用的。

To give an example that should be familiar, consider System.Diagnostics.Process .举一个应该很熟悉的例子,考虑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.它暴露了一个Exited事件,但它也暴露了一个WaitForExit方法,该方法允许调用者同步等待直到进程退出。 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.究其原因,我需要signal.Reset()是,如果一个线程调用WaitForNextEventsignal.Set()已经被调用(或者换句话说,如果.Set当没有线程在等待被称为),它会立即返回,因为事件之前已经发出信号。

The question问题

  • Is it guaranteed that a thread calling WaitForNextEvent() will be signaled before signal.Reset() is called?难道是保证一个线程调用WaitForNextEvent()信号signal.Reset()被调用? If not, what are other solutions for implementing a WaitFor method?如果不是,那么实现WaitFor方法的其他解决方案是什么?

Instead of using AutoResetEvent or ManualResetEvent , use this:而不是使用AutoResetEventManualResetEvent ,使用这个:

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

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.如果我在SetReset之间添加一个Thread.Sleep它会打印它。 Note that this is very processor dependent, so you could have to create tons of threads to "fill" the CPUs.请注意,这非常依赖于处理器,因此您可能必须创建大量线程来“填充”CPU。 On my PC it's reproducible 50% of the times :-)在我的 PC 上,它可以重复 50% 的时间:-)

For the Exited:对于退出:

readonly object mylock = new object();

then somewhere:然后某处:

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

and the WaitForExit :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)请注意, lock结构与async / await不兼容(因为几乎不是 .NET 的所有锁定原语)

I would use TaskCompletionSource s:我会使用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();
}

By the time that the code calls SetResult , no new code entering WaitForNextEvent can obtain the TaskCompletionSource that is being signalled.通过该代码调用时SetResult ,没有进入新的代码WaitForNextEvent可以获得TaskCompletionSource正在被通知。

I believe it is not guaranteed.我相信这是不能保证的。

However, your logic flow is not understood by me.但是,我不理解您的逻辑流程。 If your main thread Set s the signal, why should it wait until that signal reaches its destination?如果您的主线程Set是信号,为什么要等到该信号到达目的地? 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.如果你不能这样做,我建议你使用第二个WaitHandle来通知第一个线程第二个已经接收到信号。 But I cannot see any pros of such a strategy.但我看不到这种策略的任何优点。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM