Wait for QueueUserWorkItem to Complete

33,591

Solution 1

You could use events to sync. Like this:

private static ManualResetEvent resetEvent = new ManualResetEvent(false);

public static void Main()
{
    ThreadPool.QueueUserWorkItem(arg => DoWork());
    resetEvent.WaitOne();
}

public static void DoWork()
{
    Thread.Sleep(5000);
    resetEvent.Set();
}

If you don't want to embed event set into your method, you could do something like this:

var resetEvent = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem(
    arg => 
    {
        DoWork();
        resetEvent.Set();
    });
resetEvent.WaitOne();

For multiple items:

var events = new List<ManualResetEvent>();

foreach(var job in jobs)
{   
    var resetEvent = new ManualResetEvent(false);
    ThreadPool.QueueUserWorkItem(
        arg =>
        {
            DoWork(job);
            resetEvent.Set();
        });
    events.Add(resetEvent);
}
WaitHandle.WaitAll(events.ToArray());

Solution 2

The best way to do this is to use the CountdownEvent class. This is a fairly well established pattern and is about as scalable as it gets.

using (var finished = new CountdownEvent(1))
{
  foreach (var workitem in workitems)
  {
    var capture = workitem; // Used to capture the loop variable in the lambda expression.
    finished.AddCount(); // Indicate that there is another work item.
    ThreadPool.QueueUserWorkItem(
      (state) =>
      {
        try
        {
          ProcessWorkItem(capture);
        }
        finally
        {
          finished.Signal(); // Signal that the work item is complete.
        }
      }, null);
  }
  finished.Signal(); // Signal that queueing is complete.
  finished.Wait(); // Wait for all work items to complete.
}

Solution 3

You can use .NET's Barrier class to achieve this.

Barrier barrier = new Barrier(3); 
for(int i = 0; i < 2; i++)
{
    ThreadPool.QueueUserWorkItem(
    (state) =>
    {
       foo();
       barrier.SignalAndWait();
    }, null);
}
barrier.SignalAndWait();
Share:
33,591

Related videos on Youtube

PedroC88
Author by

PedroC88

Project manager and developer, usually dance to the music in my own head, father, daydreamer, idealist and member of Tech Digital Magazine Linkeado

Updated on February 03, 2020

Comments

  • PedroC88
    PedroC88 about 4 years

    If I add jobs to the thread pool with QueueUserWorkItem... how do I keep my program from going forward until all jobs are completed?

    I know I could add some logic to keep the app from running until all jobs are completed, but I want to know if there is something like Thread.Join() or if there's any way to retrieve each thread that is being assigned a job.

    • Chris Shain
      Chris Shain over 12 years
      Which version of .NET are you using?
  • PedroC88
    PedroC88 almost 13 years
    This would easily work for queuing one job at a time... but If I had multiple jobs then I would need a buckle to control it? Wouldn't I? If not... How is that adaptable to multiple queued jobs?
  • Bas Smit
    Bas Smit about 10 years
    this has a limit of 64 items. As the other answers show there are more suitable constructs for dealing with multiple tasks, but if you are targeting older profiles you can use a single event and use a counter decremented with Interlocked.Decrement
  • newprint
    newprint over 9 years
    It is great solution, unfortunately it only works in .NET 4 and up.
  • John Odom
    John Odom almost 9 years
    @BrianGideon Excellent answer! But how would I be able to cancel the threads prematurely with CountdownEvent? Like if I was using this in a Windows Forms application and the user closes the form but there are threads running.
  • Brian Gideon
    Brian Gideon almost 9 years
    @JohnOdom: This answer is a bit outdated. A more contemporary solution is to use the Task Parallel Library for stuff like this. Anyway, included in the TPL is the cooperative cancellation classes. That's what should be used to gracefully terminate threads.
  • Mike
    Mike about 8 years
    DrewR, could you provide an example that answers the question?
  • Drew R
    Drew R about 8 years
    I added an example - note this is a .NET 4 feature
  • Dima
    Dima over 4 years
    @BrianGideon what would you recommend from TPL for this case?
  • Jay Brown
    Jay Brown about 3 years
    The UI thread freezes when I use the resetEvent.WaitOne() method. How can I avoid that?