I have what I assume is a pretty common threading scenario:
Even though the other answers are nice if you want another option (you can never have enough options), then how about this as an idea.
Just put the data for each job into a structure, which is in a FIFO stack.
Create 15 threads.
Each thread will get the next job from the stack, popping it off.
When a thread finishes the processing, get the next job, if the stack is empty the thread dies or just sleeps, waiting.
The only complexity, which is pretty simple to resolve, is having the popping be in a critical section (synchronize read/pop).
When you queue a work item in the thread queue, you should get a waithandle back. Put them all in an array and you can pass it as an argument to the WaitAll()
function.
Here's the pseudocode of how I would approach it (this doesn't leverage ThreadPool, so someone might have a better answer:)
main
{
create queue of 100 jobs
create new array of 15 threads
start threads, passing each the job queue
do whatever until threads are done
}
thread(queue)
{
while(queue isn't empty)
{
lock(queue) { if queue still isn't empty dequeue a thing }
process the thing
}
queue is empty so exit thread
}
EDIT: If your issue is how to tell when the threads are finished, and you're using normal C# threads, (not ThreadPooled threads) you can call Thread.Join() on each thread with an optional timeout and it will return only once the thread is done. If you want to keep track of just how many threads are done without getting hung up on one, you can cycle through them in a way like this:
for(int i = 0; allThreads.Count > 0; i++)
{
var thisThread = allThreads[i % threads.Count];
if(thisThread.Join(timeout)) // something low, maybe 100 ms or something
allThreads.Remove(thisThread);
}
Microsoft's Reactive Framework is superb for this:
Action[] jobs = new Action[100];
var subscription =
jobs
.ToObservable()
.Select(job => Observable.Start(job))
.Merge(15)
.Subscribe(
x => Console.WriteLine("Job Done."),
() => Console.WriteLine("All Jobs Done."))
Done.
Just NuGet "System.Reactive".
I would just use the Task Parallel Library.
You can do this as a single, simple Parallel.For loop with your tasks, and it will automatically manage this fairly cleanly. If you can't wait for C# 4 and Microsoft's implementation, a temporary workaround is to just compile and use the Mono Implementation of TPL. (I personally prefer the MS implementation, especially the newer beta releases, but the Mono one is functional and redistributable today.)
ThreadPool
might be the way to go. The SetMaxThreads
method would be able to restrict the number of threads which are being executed. However, this restricts the maximum number of threads for the process/AppDomain. I wouldn't suggest using SetMaxThreads
if the process is running as a service.
private static ManualResetEvent manual = new ManualResetEvent(false);
private static int count = 0;
public void RunJobs( List<JobState> states )
{
ThreadPool.SetMaxThreads( 15, 15 );
foreach( var state in states )
{
Interlocked.Increment( count );
ThreadPool.QueueUserWorkItem( Job, state );
}
manual.WaitOne();
}
private static void Job( object state )
{
// run job
Interlocked.Decrement( count );
if( Interlocked.Read( count ) == 0 ) manual.Set();
}