Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I accomplish ThreadPool.Join?

I am writing a windows service that uses ThreadPool.QueueUserWorkItem(). Each thread is a short-lived task.

When the service is stopped, I need to make sure that all the threads that are currently executing complete. Is there some way of waiting until the queue clears itself?

like image 333
recursive Avatar asked Aug 06 '10 18:08

recursive


People also ask

How do you implement a ThreadPool?

To use thread pools, we first create a object of ExecutorService and pass a set of tasks to it. ThreadPoolExecutor class allows to set the core and maximum pool size. The runnables that are run by a particular thread are executed sequentially.

How do you wait for ThreadPool to finish?

You can wait for a task to finish in a ThreadPoolExecutor by calling the wait() module function.

What does a ThreadPool do?

The thread pool creates and destroys worker threads in order to optimize throughput, which is defined as the number of tasks that complete per unit of time. Too few threads might not make optimal use of available resources, whereas too many threads could increase resource contention. You can use the ThreadPool.

Does TPL use ThreadPool?

There are several benefits of using TPL over threads: It autoscales the concurrency to a multicore level. It autoscales LINQ queries to a multicore level. It handles the partitioning of the work and uses ThreadPool where required.


2 Answers

You could create an event (e.g. ManualResetEvent) in each thread, and keep it in a synchronised list (using the lock construct). Set the event or remove it from the list when the task is finished.

When you want to join, you can use WaitHandle.WaitAll (MSDN documentation) to wait for all the events to be signalled.

It's a hack, but I can't see how to reduce it to anything simpler!


Edit: additionally, you could ensure that no new events get posted, then wait a couple of seconds. If they are indeed short-lived, you'll have no problem. Even simpler, but more hacky.

Finally, if it's just a short amount of time, the service won't exit until all threads have died (unless they are background threads); so if it's a short amount of time, the service control manager won't mind a second or so - you can just leave them to expire - in my experience.

like image 164
Kieren Johnstone Avatar answered Sep 18 '22 22:09

Kieren Johnstone


The standard pattern for doing this is to use a counter which holds the number of pending work items and one ManualResetEvent that is signalled when the counter reaches zero. This is generally better than using a WaitHandle for each work item as that does not scale very well when there are a lot of simultaneous work items. Plus, some of the static WaitHandle method only accept a maximum of 64 instances anyway.

// Initialize to 1 because we are going to treat the current thread as
// a work item as well. This is to avoid a race that could occur when
// one work item gets queued and completed before the next work item
// is queued.
int count = 1;
var finished = new ManualResetEvent(false); 
try 
{ 
  while (...)
  {  
    Interlocked.Increment(ref counter);
    ThreadPool.QueueUserWorkItem( 
      delegate(object state) 
      { 
        try 
        { 
          // Your task goes here. 
        } 
        finally 
        { 
          // Decrement the counter to indicate the work item is done.
          if (Interlocked.Decrement(ref count) == 0) 
          { 
            finished.Set(); 
          } 
        } 
      }); 
  } 
}
finally
{
  // Decrement the counter to indicate the queueing thread is done.
  if (Interlocked.Decrement(ref count) == 0) 
  { 
    finished.Set(); 
  } 
}
finished.WaitOne(); 
like image 39
Brian Gideon Avatar answered Sep 19 '22 22:09

Brian Gideon