AutoResetEvent在Set之后立即重置

时间:2013-09-04 08:47:45

标签: c# multithreading 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() (或者换句话说,如果{{1} <}没有线程在等待时被调用),它会立即返回,因为事件已经先前已经发出信号。

问题

  • 是否可以确保在调用 .Set之前发出呼叫WaitForNextEvent()的话题?如果没有,那么实施signal.Reset()方法的其他解决方案是什么?

4 个答案:

答案 0 :(得分:6)

请使用以下代码:

,而不是使用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();
}

然后改变你的代码:

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

答案 1 :(得分:1)

无法保证。这样:

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.SleepSet之间添加Reset,则会将其打印出来。请注意,这与处理器有关,因此您可能必须创建大部分线程来“填充”CPU。请注意,在我的电脑上,它可以在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的所有锁定原语)

答案 2 :(得分:1)

我会使用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

答案 3 :(得分:0)

我相信不能保证。

但是,我的逻辑流程并不理解。如果主线程Set是信号,为什么要等到信号到达目的地?在等待的那个线程中继续你的“后置信号设置”逻辑不是更好吗?

如果你不能这样做,我建议你使用第二个WaitHandle来发信号通知第二个线程已经发出信号。但我看不出这种策略的任何优点。