Something like that?
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>();
EDIT
Keep in mind that the above code will prevent multiple threads from starting at once. If the threads are long, then it is still possible to simultaneously execute multiple threads. For example, if you start 5 threads per second, but each thread runs for 1 second, then at any given time in 10 seconds 10 threads will theoretically execute.
If you want you to never have more than 5 threads running right away, the easiest way is to drop the custom Limiter class and just use Semaphore directly.
const int maxThreadCount = 5; static Semaphore counter = new Semaphore(maxThreadCount, maxThreadCount); static void NewThread(object state){ counter.WaitOne();
Now it's about as simple as it can be. One caveat: creating new threads and sleeping them immediately is generally considered a bad idea. It uses system resources to create a thread that does nothing. It is better to queue requests and start new threads (or, even better, use streams of thread threads) to process them only when they have the right to start. It is harder and harder to achieve. Depending on the design, an additional flow for the scheduler / control may be required. Something like that:
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(); } }
In this class, I removed the singleton property and set the constructor a maxThreadCount . This avoids the lack of thread safety in a first class property. There are other ways to ensure thread safety, but this was the easiest.
source share