10

考虑以下模式:

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 after signal.Set()已经被调用(或者换句话说,如果.Set在没有线程等待时被调用),它会立即返回,因为该事件之前已经发出信号。

问题

  • 是否保证在调用线程调用之前WaitForNextEvent()会发出信号?如果没有,实现方法的其他解决方案是什么? signal.Reset()WaitFor
4

4 回答 4

8

而不是使用AutoResetEventor 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();
}
于 2013-09-04T10:29:54.073 回答
2

没有任何保证。这个:

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在 theSet和 the之间添加一个Reset它会打印它。请注意,这非常依赖于处理器,因此您可能必须创建大量线程来“填充” CPU。在我的 PC 上,它可以重现 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 的锁定原语)

于 2013-09-04T08:54:10.557 回答
1

我会使用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();
}

到代码调用的时候SetResult,没有新的代码输入WaitForNextEvent可以得到TaskCompletionSource正在发出的信号。

于 2013-09-04T10:46:42.463 回答
0

我相信这是不能保证的。

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

如果你不能这样做,我建议你使用 secondWaitHandle来通知第一个线程,第二个线程已经接收到信号。但我看不出这种策略有什么好处。

于 2013-09-04T08:54:56.833 回答