像这样的东西吗?
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
范围。这样就避免了第一类属性中线程安全性的缺失。还有其他方法可以添加线程安全,但这是最简单的。