急切地处理ManualResetEvent

SLa*_*aks 6 .net c# thread-safety

我有一个类允许其他线程等到它使用a完成一个操作ManualResetEventSlim.(操作通常很简短)

这个类没有明确的生命周期,因此没有一个地方可以轻松关闭事件.
相反,我希望在事件结束后立即关闭事件 - 一旦发出信号,并且在任何等待的线程唤醒之后.

出于性能原因,我宁愿不使用锁.

这段代码是否是线程安全的,是否可以更快?

volatile bool isCompleted;
volatile int waitingCount;
ManualResetEventSlim waiter = new ManualResetEventSlim();

//This method is called on any thread other than the one that calls OnCompleted
public void WaitForCompletion() {
    if (isCompleted)
        return;

    Interlocked.Increment(ref waitingCount);
    Thread.MemoryBarrier();
    if (!isCompleted)
        waiter.Wait();

    if (0 == Interlocked.Decrement(ref waitingCount)) {
        waiter.Dispose();
        waiter = null;
    }
    return;
}

//This method is called exactly once.
protected internal virtual void OnCompleted(string result) {
    Result = result;
    isCompleted = true;
    Thread.MemoryBarrier();
    if (waitingCount == 0) {
        waiter.Dispose();
        waiter = null;
    } else
        waiter.Set();
}
Run Code Online (Sandbox Code Playgroud)

use*_*116 1

我在您的代码中看到的最重要的事情是调用后waiterto的设置。我在我负责的非托管接口上有大量托管包装器,当我迁移到 .Net 4.0 时,这种做法在某些线程场景中又给我带来了困扰。nullDispose

MSDN 上的信息ManualResetEventSlim.Dispose表明它不是线程安全的,但是,查看其实际实现,Dispose从多个线程进行多次调用并没有什么危险。此外, 的实现IDisposable应该能够非常容忍多次调用(如其设计指南中所指定)。

我曾经考虑过的一个想法是OnCompleted稍微重新排序,以允许读者在完成后不久订阅:

//This method is called exactly once.
protected internal virtual void OnCompleted(string result) {
    Result = result;
    isCompleted = true;

    waiter.Set();
    Thread.MemoryBarrier();
    if (waitingCount == 0) {
        waiter.Dispose();
    }
}
Run Code Online (Sandbox Code Playgroud)