views:

1701

answers:

6

If you have one main thread that starts two other threads. what is the cleanest way to make the primary thread wait for the two other threads?

I could use bgndworker and sleep spinner that checks for both the bgnd workers's IsBusy, but I would think there's a better way.

EDIT Some more requirements:

  • The main thread has some other work to do (e.g. GUI).
  • The two spawned threads should be able to report exceptions and return result values
A: 

WaitHandle.WaitAny is your friend. It lets you wait on multiple threads or other kinds of objects to be signaled.

http://msdn.microsoft.com/en-us/library/tdykks7z.aspx

EDIT:

Actually, WaitHandle.WaitAll is what you want, not WaitAny.

http://msdn.microsoft.com/en-us/library/system.threading.waithandle.waitall.aspx

EDIT:

The Join method some mention is ok, but one drawback is that it's not an completely atomic operation. This may or may not be important to you, though.

Mystere Man
In my current situation it doesn't matter THAT much, but I try to get a main idea out of every question so I can apply it later on when other problems occure. So yes, it is important in some way.
borisCallens
+2  A: 
thread1.Join();
thread2.Join();
James L
This requires the vanilla thread and doesn't allow me to bubble exceptions etc. Does it not?
borisCallens
It was an excellent answer before he added "Additional requirements". He must be a user ...
C. Ross
Yes - perhaps a second question would have been appropriate
James L
+4  A: 

Quick example using Thread.Join();

        Thread t1 = new Thread(new ThreadStart(delegate()
        {
            System.Threading.Thread.Sleep(2000);
        }));

        Thread t2 = new Thread(new ThreadStart(delegate()
        {
            System.Threading.Thread.Sleep(4000);
        }));

        t1.Start();
        t2.Start();

        t1.Join();
        t2.Join();

EDIT Another 3example using Wait Handles:

            ManualResetEvent[] waitHandles = new ManualResetEvent[]{
            new ManualResetEvent(false),
            new ManualResetEvent(false)
        };

        Thread t1 = new Thread(new ParameterizedThreadStart(delegate(object state)
        {
            ManualResetEvent handle = (ManualResetEvent)state;
            System.Threading.Thread.Sleep(2000);
            handle.Set();
        }));

        Thread t2 = new Thread(new ParameterizedThreadStart(delegate(object state)
        {
            ManualResetEvent handle = (ManualResetEvent)state;
            System.Threading.Thread.Sleep(4000);
            handle.Set();
        }));

        t1.Start(waitHandles[0]);
        t2.Start(waitHandles[1]);

        WaitHandle.WaitAll(waitHandles);

        Console.WriteLine("Finished");
REA_ANDREW
The second exaple could help me out, I'll have a look into it
borisCallens
Is there a way to use the resetevents with the backgroundworker?
borisCallens
Subscribe to the BackgroundWorker's RunWorkerCompleted which is fired when the task is complete, cancelled or raises an exception
REA_ANDREW
That would require my ManualResetEvent[] to be global.
borisCallens
Ok, another option i could advise which is still asyncronous, is the MethodInvoker class which you code a callback method which is called when the function completes MethodInvoker m = new MethodInvoker(MyMethod); BeginInvoke and EndInvoke . You are able to provide state too. Sounds more suited
REA_ANDREW
+1  A: 

In addition to the other answers... one alternative to "start two threads and wait for both to finish" is "start one thread and do one job yourself, then wait for the other thread to finish".

Of course, if you want to start two jobs, do some more work on the main thread and then wait for the two other threads to finish, that doesn't work so well.

EDIT: If the main thread is a UI thread, you shouldn't be blocking on the other threads finishing - you should (IMO) get them to call back to the main thread when they've finished. That way you'll still have a responsive UI while they're executing.

Jon Skeet
Yes, valid point. But maybe I oversimplified the question. The main thread will be busy with updating the UI so can't be bothered with the IO heavy tasks.
borisCallens
The main thread is a Console thread. It will show some repetetive "Waiting..." message. But I follow you on events over spinning. See my other question: http://stackoverflow.com/questions/594329/define-backgroundworkers-runworkercompleted-with-an-anonymous-method Or am I going completely off track?
borisCallens
+1  A: 

See the answers on this thread. I like this option ;-p

Forker p = new Forker();
p.Fork(delegate { DoSomeWork(); });
p.Fork(delegate { DoSomeOtherWork(); });
p.Join();

Re returning values / reporting exceptions - just have each fork do that as a callback at the end of the logic... (you can use captured variables to pass state into both the forks, including a shared logger etc).

Marc Gravell
+1  A: 

You can't wait for finish and have GUI usable. The best approach for this is to spawn threads and use events to communicate with GUI. Remember, that in the event handler you cannot modify control. Rather that doing it directly, use Invoke method.

Migol
Usually also one needs some info back to the GUI so that the use of events for progress info cannot be avoided if you don't want the thread to be autistic. This answer is the best I could read on this page thanks!
jdehaan