编码/设计通用线程安全限制器(即将 X() 每秒执行多次)

2024-06-24

我计划设计一个类来将函数的执行限制在指定时间内的给定数量,例如:

  • 最大处理量1 秒内 5 个文件

它应该是线程安全的并且性能影响应该最小。

你会如何设计这样一个类?我有几个想法,但对我来说,没有一个是正确的。

是否有任何已知的设计模式可以完成此类任务? (我正在编写 .NET 代码,但任何语言都可以)

从外部班级应该像这样工作(假设它是单例):

Setup:

Limiter.Instance.MaxExecutionPerSecond = 5

然后我们会在执行我们的函数之前在线程中调用它,如果需要它会阻塞线程:

Limiter.Instance.WaitIfRequired()

像这样的东西吗?

using Timer = System.Threading.Timer;

class Limiter{
    public static readonly Limiter Instance = new Limiter();

    Limiter(){}

    int         max;
    Semaphore   counter;
    List<Timer> timers  = new List<Timer>();

    // warning: not thread safe!
    public int MaxExecutionPerSecond{
        get{return max;}
        set{counter = new Semaphore(max = value, value);}
    }

    public void WaitIfRequired(){
        // Our semaphore starts with a count of MaxExecutionPerSecond.
        // When we call WaitOne(), it decrements the count.  If the count
        // is already zero, the call to WaitOne() will block until another
        // thread calls Release() to increment the count.
        counter.WaitOne();

        // Set a timer to increment the semaphore in one second.
        Timer t = null;
        t = new Timer(o=>{
            // Increment the semaphore.
            counter.Release();

            // We no longer need to protect this timer from the GC.
            timers.Remove(t);
            t.Dispose();
        });

        // Hold a reference to this timer to keep it from being disposed of.
        timers.Add(t);

        // Set the timer to release the semaphore in one second.
        t.Change(1000, Timeout.Infinite);
    }
}

EDIT

要记住的一件事是上面的代码只会阻止许多线程starting立刻。如果线程长时间运行,那么仍然可能有很多线程running立刻。例如,如果每秒启动 5 个线程,但每个线程运行 1 秒,那么在 2 秒后的任何给定时间,理论上都会有 10 个线程在运行。

如果你想确保永远不会同时运行超过 5 个线程,最简单的方法就是放弃自定义Limiter类,只需使用Semaphore直接地。

const int maxThreadCount = 5;
static Semaphore counter = new Semaphore(maxThreadCount, maxThreadCount);

static void NewThread(object state){
    counter.WaitOne();

    // do something

    counter.Release();
}

现在就这么简单了。但需要注意的是:创建新线程并立即休眠它们通常被认为是一个坏主意。这会使用系统资源创建一个不执行任何操作的线程。最好将请求排队并启动新线程(或者更好的是,使用线程池线程)仅在它们有资格运行时处理它们。这更加复杂,也更难做到正确。根据设计,可能需要额外的线程来进行调度/管理。像这样的事情:

class Limiter{
    class WorkData{
        readonly ParameterizedThreadStart action;
        readonly object                   data;

        public ParameterizedThreadStart Action{get{return action;}}
        public object                   Data  {get{return data;}}

        public WorkData(ParameterizedThreadStart action, object data){
            this.action = action;
            this.data   = data;
        }
    }

    readonly Semaphore       threadCount;
    readonly Queue<WorkData> workQueue   = new Queue<WorkData>();
    readonly Semaphore       queueCount  = new Semaphore(0, int.MaxValue);

    public Limiter(int maxThreadCount){
        threadCount = new Semaphore(maxThreadCount, maxThreadCount);
        Thread t = new Thread(StartWorkItems);
        t.IsBackground = true;
        t.Start();
    }

    void StartWorkItems(object ignored){
        while(queueCount.WaitOne() && threadCount.WaitOne()){
            WorkData wd;
            lock(workQueue)
                wd = workQueue.Dequeue();

            ThreadPool.QueueUserWorkItem(DoWork, wd);
        }
    }
    void DoWork(object state){
        WorkData wd = (WorkData)state;
        wd.Action(wd.Data);
        counter.Release();
    }

    public void QueueWork(ParameterizedThreadStart action, object data){
        lock(workQueue)
            workQueue.Enqueue(new WorkData(action, data));
        queueCount.Release();
    }
}

在此类中,我删除了单例属性并为构造函数提供了maxThreadCount范围。这样就避免了第一类属性中线程安全性的缺失。还有其他方法可以添加线程安全,但这是最简单的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

编码/设计通用线程安全限制器(即将 X() 每秒执行多次) 的相关文章

随机推荐