线程安全递减计数器
本文关键字:计数器 安全 线程 | 更新日期: 2023-09-27 18:19:45
问题
在一个项目案例中,我需要创建多个线程,这些线程从队列中挑选任务并运行它们。如果一组其他任务仍在运行,则其中一些任务无法运行。考虑一下windows中的文件复制和碎片整理(在系统空闲时运行)。
解决方案
为了实现这一点,我创建了一个基于System.Threading.CountdownEvent.
每当线程从队列中选择一个阻塞任务时,它们都会Increment
和CounterEvent
,在完成作业后,它们会Decrement
和CounterEvent
。
如果线程选择了一个低优先级任务,它将Wait
,直到CounterEvent
为零,然后开始运行。
低优先级taks可以立即从CounterEvent
的Reset
开始
主线程或并行线程可以通过查询CurrentCount
来监控锁的状态。
这是代码:
using System;
using System.Diagnostics.Contracts;
using System.Threading;
public class CounterEvent : IDisposable {
private volatile int m_currentCount;
private volatile bool m_disposed;
private ManualResetEventSlim m_event;
// Gets the number of remaining signals required to set the event.
public int CurrentCount {
get {
return m_currentCount;
}
}
// Allocate a thin event, Create a latch in signaled state.
public CounterEvent() {
m_currentCount = 0;
m_event = new ManualResetEventSlim();
m_event.Set(); //
}
// Decrements the counter. if counter is zero signals other threads to continue
public void Decrement() {
ThrowIfDisposed();
Contract.Assert(m_event != null);
int newCount = 0;
if (m_currentCount >= 0) {
#pragma warning disable 0420
newCount = Interlocked.Decrement(ref m_currentCount);
#pragma warning restore 0420
}
if (newCount == 0) {
m_event.Set();
}
}
// increments the current count by one.
public void Increment() {
ThrowIfDisposed();
#pragma warning disable 0420
Interlocked.Increment(ref m_currentCount);
#pragma warning restore 0420
}
// Resets the CurrentCount to the value of InitialCount.
public void Reset() {
ThrowIfDisposed();
m_currentCount = 0;
m_event.Set();
}
// Blocks the current thread until the System.Threading.CounterEvent is set.
public void Wait() {
ThrowIfDisposed();
m_event.Wait();
}
/// <summary>
/// Throws an exception if the latch has been disposed.
/// </summary>
private void ThrowIfDisposed() {
if (m_disposed) {
throw new ObjectDisposedException("CounterEvent");
}
}
// According to MSDN this is not thread safe
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
// According to MSDN Dispose() is not thread-safe.
protected virtual void Dispose(bool disposing) {
if (disposing) {
m_event.Dispose();
m_disposed = true;
}
}
}
问题
这个代码能按预期工作吗?有什么我没有看到的缺点吗?有更好的选择吗?
注意
应用程序是用System.Threading.Thread编写的,对我来说转换它的成本很高,但一个很好的替代解决方案总是值得为未来努力。
这应该是一个原子操作,如果你像这样做,它是不安全的
if (m_currentCount >= 0)
{
newCount = Interlocked.Decrement(ref m_currentCount);
}
m_currentCount
可能在if
和Interlocked.Decrement
之间发生变化你应该重写你的逻辑以使用Interlocked.CompareExchange
我也会在你分配给m_currentCount
的每个地方使用Interlocked.Exchange
,然后你就不需要volatile
和pragma
了。你还应该意识到,在非常重的负载下,可能会发生重置事件Set
丢失