急切地处理ManualResetEvent
我有一个类允许其他线程等到它使用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(); }
我在代码中看到的最重要的事情是在调用Dispose
后将服务器设置为null
。 我负责管理非托管接口上的大量托管包装,当我转移到.Net 4.0时,这种做法在某些线程场景中回过头来咬我。
ManualResetEventSlim.Dispose
上的MSDN信息表明它不是线程安全的,但是,查看其实际实现,从多个线程多次调用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(); } }
更糟糕的是,有些条件下根本不会处理服务员。 如果在 waitingCount > 0
时调用OnCompleted
,则isCompleted
标志将设置为true
,但服务器将不会被waitingCount > 0
。当某些东西调用 WaitForCompletion
,它会看到isCompleted
为true
,并立即退出。waiter.Dispose
永远不会被调用。
为什么不使用像SpinLock这样的东西 ,它使用与ManualResetEventSlim
相同的逻辑? 如果您的等待通常非常短,那么锁可能不会争用,这是一个巨大的胜利。 如果等待很长,那么ManualResetEventSlim
无论如何都要付出内核转换的代价。
您是否确定使用锁定会非常昂贵? 有“知道”,然后有测量。 。 。