线程安全递减计数器

本文关键字:计数器 安全 线程 | 更新日期: 2023-09-27 18:19:45

问题

在一个项目案例中,我需要创建多个线程,这些线程从队列中挑选任务并运行它们。如果一组其他任务仍在运行,则其中一些任务无法运行。考虑一下windows中的文件复制和碎片整理(在系统空闲时运行)

解决方案

为了实现这一点,我创建了一个基于System.Threading.CountdownEvent.

每当线程从队列中选择一个阻塞任务时,它们都会IncrementCounterEvent,在完成作业后,它们会DecrementCounterEvent

如果线程选择了一个低优先级任务,它将Wait,直到CounterEvent为零,然后开始运行。

低优先级taks可以立即从CounterEventReset开始

主线程或并行线程可以通过查询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可能在ifInterlocked.Decrement之间发生变化你应该重写你的逻辑以使用Interlocked.CompareExchange我也会在你分配给m_currentCount的每个地方使用Interlocked.Exchange,然后你就不需要volatilepragma了。你还应该意识到,在非常重的负载下,可能会发生重置事件Set丢失