199

I've never really used threading before in C# where I need to have two threads, as well as the main UI thread. Basically, I have the following.

public void StartTheActions()
{
  // Starting thread 1....
  Thread t1 = new Thread(new ThreadStart(action1));
  t1.Start();

  // Now, I want for the main thread (which is calling `StartTheActions` method)
  // to wait for `t1` to finish. I've created an event in `action1` for this.
  // The I wish `t2` to start...

  Thread t2 = new Thread(new ThreadStart(action2));
  t2.Start();
}

So, essentially, how can I have a thread wait for another one to finish? What is the best way to do this?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Maxim Zaslavsky
  • 17,787
  • 30
  • 107
  • 173
  • 5
    If you are just waiting for thread 1 to finish anyways, why are you not just calling that method synchronously? – Svish Feb 09 '10 at 11:14
  • 14
    What's the point in using threads when you're processing in a linear fashion? – John Feb 12 '10 at 16:35
  • 3
    @John, it makes total sense to me that there are many uses for spinning off a background thread that works while the user works. Also, isn't your question the same as the previous one? – Sam Hobbs Jan 15 '17 at 00:08
  • [Rotem's answer](https://stackoverflow.com/a/14119920/2268680), using backgroundworker for easy usage, it's very simple. – Kamil Sep 06 '18 at 02:42

11 Answers11

285

I can see five options available:

1. Thread.Join

As with Mitch's answer. But this will block your UI thread, however you get a Timeout built in for you.


2. Use a WaitHandle

ManualResetEvent is a WaitHandle as jrista suggested.

One thing to note is if you want to wait for multiple threads: WaitHandle.WaitAll() won't work by default, as it needs an MTA thread. You can get around this by marking your Main() method with MTAThread - however this blocks your message pump and isn't recommended from what I've read.


3. Fire an event

See this page by Jon Skeet about events and multi-threading. It's possible that an event can become unsubcribed between the if and the EventName(this,EventArgs.Empty) - it's happened to me before.

(Hopefully these compile, I haven't tried)

public class Form1 : Form
{
    int _count;

    void ButtonClick(object sender, EventArgs e)
    {
        ThreadWorker worker = new ThreadWorker();
        worker.ThreadDone += HandleThreadDone;

        Thread thread1 = new Thread(worker.Run);
        thread1.Start();

        _count = 1;
    }

    void HandleThreadDone(object sender, EventArgs e)
    {
        // You should get the idea this is just an example
        if (_count == 1)
        {
            ThreadWorker worker = new ThreadWorker();
            worker.ThreadDone += HandleThreadDone;

            Thread thread2 = new Thread(worker.Run);
            thread2.Start();

            _count++;
        }
    }

    class ThreadWorker
    {
        public event EventHandler ThreadDone;

        public void Run()
        {
            // Do a task

            if (ThreadDone != null)
                ThreadDone(this, EventArgs.Empty);
        }
    }
}

4. Use a delegate

public class Form1 : Form
{
    int _count;

    void ButtonClick(object sender, EventArgs e)
    {
        ThreadWorker worker = new ThreadWorker();

        Thread thread1 = new Thread(worker.Run);
        thread1.Start(HandleThreadDone);

        _count = 1;
    }

    void HandleThreadDone()
    {
        // As before - just a simple example
        if (_count == 1)
        {
            ThreadWorker worker = new ThreadWorker();

            Thread thread2 = new Thread(worker.Run);
            thread2.Start(HandleThreadDone);

            _count++;
        }
    }

    class ThreadWorker
    {
        // Switch to your favourite Action<T> or Func<T>
        public void Run(object state)
        {
            // Do a task

            Action completeAction = (Action)state;
            completeAction.Invoke();
        }
    }
}

If you do use the _count method, it might be an idea (to be safe) to increment it using

Interlocked.Increment(ref _count)

I'd be interested to know the difference between using delegates and events for thread notification, the only difference I know are events are called synchronously.


5. Do it asynchronously instead

The answer to this question has a very clear description of your options with this method.


Delegate/Events on the wrong thread

The event/delegate way of doing things will mean your event handler method is on thread1/thread2 not the main UI thread, so you will need to switch back right at the top of the HandleThreadDone methods:

// Delegate example
if (InvokeRequired)
{
    Invoke(new Action(HandleThreadDone));
    return;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Chris S
  • 64,770
  • 52
  • 221
  • 239
70

Add

t1.Join();    // Wait until thread t1 finishes

after you start it, but that won't accomplish much as it's essentialy the same result as running on the main thread!

I can highly recommended reading Joe Albahari's Threading in C# free e-book, if you want to gain an understanding of threading in .NET.

Mitch Wheat
  • 295,962
  • 43
  • 465
  • 541
  • 4
    Even though `Join` is literally what the asker seems to have been asking for, this can be extremely bad in general. A call to `Join` will hang up the thread from which this being done. If that happens to be the main GUI thread, this is *BAD*! As a user I actively loathe applications that seem to work this way. So please see all the other answers to this question and http://stackoverflow.com/questions/1221374/how-can-i-improve-the-subjective-speed-of-my-application/1221420#1221420 – peSHIr Feb 09 '10 at 11:30
  • 1
    I agree that in general Join() is bad. I perhaps didn't make that obvious enough in my answer. – Mitch Wheat Aug 12 '11 at 00:37
  • 3
    Guys, *one size does not fit all*. There are situations, when one really needs to be sure, that thread has finished its work: consider, that the thread processes the data, which are just about to be changed. In such case notifying the thread to cancel gracefully and waiting till it finishes (especially, when one step is processed very quickly) is IMO fully justified. I would rather say, that Join is *evil* (in C++ FAQ terms), ie. it shall not be used unless **really** required. – Spook Nov 10 '12 at 12:01
  • @spook: what are you talking about? This is a 3 year old question. Did you have something to add? – Mitch Wheat Nov 10 '12 at 12:02
  • 5
    I want to state clearly, that Join is a tool, that can be useful, despite the fact, that it is very often misused. There are some situations, when it will just work without any unnecessary side effects (such as stalling main GUI thread for a noticeable amount of time). – Spook Nov 10 '12 at 12:05
  • 2
    Join is a tool? I think you'll find it's a method. – Mitch Wheat Nov 10 '12 at 12:07
  • Yes, it's a method being a tool for thread synchronization. Dangerous tool if misused, but sometimes very helpful. I want to stand against calling some methods *bad* if they are dangerous. Join should not be forbidden at all, it just has to be used with prior careful consideration. – Spook Nov 10 '12 at 12:09
  • You're making a non-point. And explaining things to those who know how they work. I'm not advocating that Join() is a good idea (which you seem to be missing). It's simply an answer to this (old) question. You seem to be missing the piunt. Let me guess: you use Join() alot? – Mitch Wheat Nov 10 '12 at 12:11
  • Firstly, my comment comments your comments, not the real answer ;) Secondly, I indeed might have misinterpreted you meaning of Join being "bad" - if so, I apologize. Thirdly, I'm just about to use Join for the first time after two and a half years of programming in C#, so I'd rather say no, I don't use Join() alot :) – Spook Nov 10 '12 at 12:55
  • 1
    I found it useful in my tests when I have 2 threads that should "wait" for each other. I can check conditions to confirm the first thread did its job and then join() to wait till the second one is finishes to do the consolidated assertions. – Daniel van Heerden May 31 '17 at 06:33
40

As an alternative to threads, you can use tasks:

class Program
{
    static void Main(string[] args)
    {
        Task task1 = Task.Factory.StartNew(() => doStuff());
        Task task2 = Task.Factory.StartNew(() => doStuff());
        Task task3 = Task.Factory.StartNew(() => doStuff());
        Task.WaitAll(task1, task2, task3);
        Console.WriteLine("All threads complete");
    }

    static void doStuff()
    {
        // Do stuff here
    }
}

From: Create multiple threads and wait all of them to complete

General Grievance
  • 4,555
  • 31
  • 31
  • 45
Sayed Abolfazl Fatemi
  • 3,678
  • 3
  • 36
  • 48
  • 14
    You didn't mention anything about Threads in your answer. The question is about Threads, not Tasks. The two are not the same. – Suamere Aug 18 '17 at 15:02
  • 9
    I came with a similar question (and knowledge level) to the original poster and this answer was very valuable to me - tasks are way more suitable for what I'm doing and if I hadn't found this answer I'd have written my own terrible thread pool. – Chris Rae Oct 01 '18 at 03:24
  • 1
    @ChrisRae, so this should be a comment in the original question, not an answer like this one. – Jaime Hablutzel Jan 22 '19 at 23:33
  • As it's about this specific answer I think it probably makes more sense here. – Chris Rae Jan 23 '19 at 04:55
  • 1
    as @Suamere said, this answer is entirely unrelated to the OP question. – Glenn Slayden Jun 07 '19 at 04:23
  • 2
    It may be unrelated to the original question because it uses tasks, but it accomplishes exactly the same thing. The OP stated they had never done threading etc. in C# and may simply have been asking about threads because he was not aware of the existence of tasks. This is simple, elegant and works like a charm. On a side note if you are kicking off x amount of tasks you can WaitAll on an array of tasks. – LordWabbit Jun 19 '20 at 19:43
37

The previous two answers are great and will work for simple scenarios. There are other ways to synchronize threads, however. The following will also work:

public void StartTheActions()
{
    ManualResetEvent syncEvent = new ManualResetEvent(false);

    Thread t1 = new Thread(
        () =>
        {
            // Do some work...
            syncEvent.Set();
        }
    );
    t1.Start();

    Thread t2 = new Thread(
        () =>
        {
            syncEvent.WaitOne();

            // Do some work...
        }
    );
    t2.Start();
}

ManualResetEvent is one of the various WaitHandle's that the .NET framework has to offer. They can provide much richer thread synchronization capabilities than the simple, but very common tools like lock()/Monitor, Thread.Join, etc.

They can also be used to synchronize more than two threads, allowing complex scenarios such as a 'master' thread that coordinates multiple 'child' threads, multiple concurrent processes that are dependent upon several stages of each other to be synchronized, etc.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
jrista
  • 32,447
  • 15
  • 90
  • 130
6

You want the Thread.Join() method, or one of its overloads.

Daniel Pryden
  • 59,486
  • 16
  • 97
  • 135
4

I would have your main thread pass a callback method to your first thread, and when it's done, it will invoke the callback method on the mainthread, which can launch the second thread. This keeps your main thread from hanging while its waiting for a Join or Waithandle. Passing methods as delegates is a useful thing to learn with C# anyway.

Ed Power
  • 8,310
  • 3
  • 36
  • 42
3

This implementation is a little different from @jrista's example based on ManualResetEvent as it shows how the various options are like a red or green traffic light.

    public System.Threading.AutoResetEvent thread1done = new System.Threading.AutoResetEvent(false);

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    {
        thread1done.Set(); //set traffic light to green before threading
        StartTheActions();
    }

    public void StartTheActions()
    {
        Thread t1 = new Thread(action1);

        t1.Start();
        thread1done.WaitOne(); //traffic light is red, until thread1done.Set inside action1()

        Thread t2 = new Thread(action2);
        t2.Start();
    }
    public void action1()
    {
        Thread.Sleep(5000);
        //.... do some work
        thread1done.Set(); //work is done, set traffic light to green at thread1done.WaitOne()
    }
    public void action2()
    {
        MessageBox.Show("Now processing action2");
    }
2

Try this:

List<Thread> myThreads = new List<Thread>();

foreach (Thread curThread in myThreads)
{
    curThread.Start();
}

foreach (Thread curThread in myThreads)
{
    curThread.Join();
}
skink
  • 5,133
  • 6
  • 37
  • 58
Wendell Tagsip
  • 121
  • 1
  • 7
1

I took a little different approach. There is a counter option in previous answers, and I just applied it a bit differently. I was spinning off numerous threads and incremented a counter and decremented a counter as a thread started and stopped. Then in the main method I wanted to pause and wait for threads to complete I did.

while (threadCounter > 0)
{
    Thread.Sleep(500); // Make it pause for half second so that we don’t spin the CPU out of control.
}

This is documented in my blog post: http://www.adamthings.com/post/2012/07/11/ensure-threads-have-finished-before-method-continues-in-c/

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Adam
  • 3,615
  • 6
  • 32
  • 51
  • 4
    This is called busy waiting. Yes it works and sometimes is the best solution, but you want to avoid it if possible because it wastes CPU time – MobileMon Oct 23 '12 at 15:12
  • @MobileMon that's more of a guideline than a rule. In this case since that loop might waste 0.00000001% of the CPU and the OP is coding in C#, replacing this with something 'more efficient' would be a complete waste of time. The first rule of optimization is - don't. Measure first. – Spike0xff Jul 26 '16 at 21:00
  • The link is broken (*"Yaiks! The content you are looking for can’t be found."*) - but the domain isn't, so feel free to fix it (anyone - the OP has left the building). Dress the naked link while you are at (use the title instead of showing the URL). – Peter Mortensen Aug 14 '20 at 01:31
1

When I want the UI to be able to update its display while waiting for a task to complete, I use a while-loop that tests IsAlive on the thread:

    Thread t = new Thread(() => someMethod(parameters));
    t.Start();
    while (t.IsAlive)
    {
        Thread.Sleep(500);
        Application.DoEvents();
    }

0

Another method is using lock(someObject) and Monitor.Wait(someObject[,timeout]) in one thread and lock(someObject) and Monitor.Pulse(someObject) in another thread. SomeObject has to be the same instance of a class in all 4 calls. SomeObject can't be a struct.

The first thread locks someObject and then calls Monitor.Wait() which releases the lock, so the second thread can lock someObject. When the second thread is finished it calls Monitor.Pulse(), and then the first thread's Monitor.Wait() ends.

Example: someObject is a queue, the first threads waits for the second to put an object in the queue and then dequeues that object.

SijeDeHaan
  • 175
  • 2
  • 2