I wanted a class which executes any number of tasks but only a certain amount at the same time (e.g. to download various internet content and keep the overall download speed at a good level). The class I wrote seems to work but there are probably things that can be improved. 1. Do these locks make sense? Should there be other locks as well? 2. I have a method and an event that is only relevant if a specific constructor is used. Is there a way to improve that? 3. The class I use for tasks is `ThreadStart`. Is that a good idea? 5. There might be better method names/class names. 4. Are there any general errors (e.g. that more Threads than the max-count will be executed)? There are probably more points that could be improved. Also, if anyone knows a good open source library (or native .NET class even) which does just what my class is supposed to do, I would be interested in that too. public class ThreadQueue { private readonly HashSet<ThreadStart> WorkingThreads = new HashSet<ThreadStart>(); private readonly Queue<ThreadStart> Queue = new Queue<ThreadStart>(); private bool RaiseCompleteEventIfQueueEmpty = false; private int ThreadsMaxCount; public ThreadQueue(int threadsMaxCount) { ThreadsMaxCount = threadsMaxCount; } /// <summary> /// Creates a new thread queue with a maximum number of threads and the tasks that should be executed. /// </summary> /// <param name="threadsMaxCount">The maximum number of currently active threads.</param> /// <param name="tasks">The tasks that should be executed by the queue.</param> public ThreadQueue(int threadsMaxCount, ThreadStart[] tasks) : this(threadsMaxCount) { RaiseCompleteEventIfQueueEmpty = true; foreach (ThreadStart task in tasks) { Queue.Enqueue(task); } } /// <summary> /// Starts to execute tasks. Used in conjunction with the constructor in which all tasks are provided. /// </summary> public void Start() { CheckQueue(); } private readonly object addlock = new object(); /// <summary> /// Adds a task and runs it if a execution slot is free. Otherwise it will be enqueued. /// </summary> /// <param name="task">The task that should be executed.</param> public void AddTask(ThreadStart task) { lock (addlock) { if (WorkingThreads.Count == ThreadsMaxCount) { Queue.Enqueue(task); } else { StartThread(task); } } } /// <summary> /// Starts the execution of a task. /// </summary> /// <param name="task">The task that should be executed.</param> private void StartThread(ThreadStart task) { WorkingThreads.Add(task); BackgroundWorker thread = new BackgroundWorker(); thread.DoWork += delegate { task.Invoke(); }; thread.RunWorkerCompleted += delegate { ThreadCompleted(task); }; thread.RunWorkerAsync(); } private void ThreadCompleted(ThreadStart start) { WorkingThreads.Remove(start); CheckQueue(); if (Queue.Count == 0 && WorkingThreads.Count == 0 && RaiseCompleteEventIfQueueEmpty) OnCompleted(); } private readonly object checklock = new object(); /// <summary> /// Checks if the queue contains tasks and runs as many as there are free execution slots. /// </summary> private void CheckQueue() { lock (checklock) { while (Queue.Count > 0 && WorkingThreads.Count < ThreadsMaxCount) { StartThread(Queue.Dequeue()); } if (Queue.Count == 0 && WorkingThreads.Count == 0 && RaiseCompleteEventIfQueueEmpty) OnCompleted(); } } /// <summary> /// Raised when all tasks have been completed. Will only be used if the ThreadQueue has been initialized with all the tasks it should execute. /// </summary> public event EventHandler Completed; /// <summary> /// Raises the Completed event. /// </summary> protected void OnCompleted() { if (Completed != null) { Completed(this, null); } } }