5

how to make the foreground thread wait for all background (child) threads to finish in C#? I need to get list of pending jobs from the queue (database), start a new thread to execute each of them and finally wait for all the child threads to finish. how to do that in C#? Thanks in advance.

RKP
  • 5,285
  • 22
  • 70
  • 111
  • 2
    possible duplicate of [C# Spawn Multiple Threads for work then wait until all finished](http://stackoverflow.com/questions/2528907/c-spawn-multiple-threads-for-work-then-wait-until-all-finished) – Adam Lear Oct 13 '10 at 13:25
  • thanks for all the quick replies to my question – RKP Oct 13 '10 at 15:14
  • have u try [EventWaitHandle();](http://msdn.microsoft.com/en-us/library/system.threading.eventwaithandle.aspx) – Bonshington Oct 13 '10 at 13:47

5 Answers5

7

You could store each launched thread in an array. Then when you need to wait for them all, call Join method on each thread in an array in a loop.

Thread child = new Thread(...);
Threads.Add(child);
child.Start()

...

foreach(Thread t in Threads)
{
   t.Join();
}

HTH

Armen Tsirunyan
  • 130,161
  • 59
  • 324
  • 434
  • Thanks for the reply. I think thread.Join method is good for a single thread or very few fixed number of threads. for multiple threads, I think there is a WaitAll method, but I couldn't find a good code example for it. – RKP Oct 13 '10 at 13:37
  • 3
    @RKP: Waiting fo all threads to finish is the same as to wait for them in turn, isn't it? There is a winapi function WaitForMultipleObjects, but it's not C#, though you can use it, but I see no point. – Armen Tsirunyan Oct 13 '10 at 13:40
2

Consider using ThreadPool. Most of what you want is already done. There is an example from Microsoft which does pretty much your entire task. Replace "fibonacci" with "database task" and it sounds like your problem.

plinth
  • 48,267
  • 11
  • 78
  • 120
  • I read this article few minutes back and was about to reply saying that is the solution I was looking for. thanks for the reply. – RKP Oct 13 '10 at 15:06
  • Last copy of above [example from Microsoft](https://web.archive.org/web/20150124003832/https://msdn.microsoft.com/en-us/library/3dasc8as(VS.80).aspx) before they took the documentation offline in 2015. The example has been moved into the docs for [ThreadPool.QueueUserWorkItem Method](https://learn.microsoft.com/en-us/dotnet/api/system.threading.threadpool.queueuserworkitem#system-threading-threadpool-queueuserworkitem(system-threading-waitcallback-system-object)) in newer versions – LeBleu Nov 30 '22 at 18:48
1

This is incomplete code, but ManualResetEvent works for you

var waitEvents = new List<ManualResetEvent>();
foreach (var action in actions)
{
    var evt = new ManualResetEvent(false);
    waitEvents.Add(evt);
    ThreadPool.RegisterWaitForSingleObject(asyncResult.AsyncWaitHandle, TimeoutCallback, state, 5000, true);
}

if (waitEvents.Count > 0)
    WaitHandle.WaitAll(waitEvents.ToArray());
Mo Patel
  • 2,321
  • 4
  • 22
  • 37
danijels
  • 5,211
  • 4
  • 26
  • 36
  • Thanks, but where the new thread for each action is started here in this code? is "5000" a timeout setting? – RKP Oct 13 '10 at 13:42
  • Look here for entire code, I just copied a bit from one of my previous answers: http://stackoverflow.com/questions/3915017/image-url-validation-in-c/3915440#3915440 – danijels Oct 13 '10 at 13:49
  • And yes, 5000 was a timeout setting – danijels Oct 13 '10 at 13:50
1

Using dynamic data you can pass your object and the WaitHandle (ActionResetEvent) that lets you wait for all the background threads to finish without declaring an extra class:

static void Main(string[] args)
{
    List<AutoResetEvent> areList = new List<AutoResetEvent>();
    foreach (MyObject o in ListOfMyObjects)
    {
        AutoResetEvent are = new AutoResetEvent(false);
        areList.Add(are);
        ThreadPool.QueueUserWorkItem(DoWork, new { o, are });
    };

    Console.WriteLine("Time: {0}", DateTime.Now);
    WaitHandle.WaitAll(areList.ToArray());
    Console.WriteLine("Time: {0}", DateTime.Now);
    Console.ReadKey();
}

static void DoWork(object state)
{
    dynamic o = state;
    MyObject myObject = (MyObject)o.o;
    AutoResetEvent are = (AutoResetEvent)o.are;

    myObject.Execute();
    are.Set();
}
Mo Patel
  • 2,321
  • 4
  • 22
  • 37
panpawel
  • 1,782
  • 1
  • 16
  • 17
0

Create a structure to keep track of your worker threads

private struct WorkerThreadElement
{
    public IAsyncResult WorkerThreadResult;
    public AsyncActionExecution WorkerThread;
}

You also need to keep track the total number of threads expected to be created and the number of threads that have currently completed

private int _TotalThreads = 0;
private int _ThreadsHandled = 0;
private List<WorkerThreadElement> _WorkerThreadElements = new List<WorkerThreadElement>();

Then create an autoreset handle in order to wait for thread completion.

// The wait handle thread construct to signal the completion of this process
private EventWaitHandle _CompletedHandle = new AutoResetEvent(false);

You also need a delegate to create new threads - There are multiple ways of doing this but i have chosen a simple delegate for the sake of this example

// Delegate to asynchronously invoke an action
private delegate void AsyncActionExecution();

Lets asume that the Invoke method is the entrance point that will create all threads and wait for their execution. So we have:

public void Invoke()
{ 
    _TotalThreads = N; /* Change with the total number of threads expected */

    foreach (Object o in objects) 
    {
        this.InvokeOneThread();
    }             

    // Wait until execution has been completed
    _CompletedHandle.WaitOne();

    // Collect any exceptions thrown and bubble them up
    foreach (WorkerThreadElement workerThreadElement in _WorkerThreadElements)
    {
        workerThreadElement.WorkerThread.EndInvoke(workerThreadElement.WorkerThreadResult);
    }
}         

InvokeOneThread is the method used to create a single thread for one operation. Here we need to create a worker thread element and invoke the actual thread.

 private void InvokeOneThread()
 {
     WorkerThreadElement threadElement = new WorkerThreadElement();
     threadElement.WorkerThread = new AsyncActionExecution();
     threadElement.WorkerThreadResult = threadElement.WorkerThread.BeginInvoke(actionParameters, InvokationCompleted, null);

     _WorkerThreadElements.Add(threadElement);
 }

Callback from thread completion

private object _RowLocker = new object();

/// <summary>
/// Increment the number of rows that have been fully processed
/// </summary>
/// <param name="ar"></param>
private void InvokationCompleted(IAsyncResult ar)
{
    lock (_RowLocker) 
    {
        _RowsHandled++; 
    }

    if (_TotalThreads == _ThreadsHandled) 
        _CompletedHandle.Set();
}

Done

Mo Patel
  • 2,321
  • 4
  • 22
  • 37
Yannis
  • 6,047
  • 5
  • 43
  • 62