How to Wait For a Thread to Finish With .Net

How can I wait for a thread to finish with .NET?

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;
}

Wait for threads to complete

I suggest to use TPL to get this done. You won't need to spawn so many threads. By default TPL will use thread pool for that:

using System;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
class Program
{
private const int TaskCount = 15;

static void Main(string[] args)
{
var tasks = new Task[TaskCount];
for (var index = 0; index < TaskCount; index++)
{
tasks[index] = Task.Factory.StartNew(Method);
}
Task.WaitAll(tasks);

Console.WriteLine("Some text");
}

private static void Method()
{
for (int i = 0; i < 15; i++)
{
Console.WriteLine(i);
}
}
}
}

C# .Net Wait for all threads to complete and get return values

Try using a Task<string> instead of a Task and querying the Task<string>.Result

How to wait for thread complete before continuing?

How much order do you need to impose on the threads? If you just need all of the work started in the loop to finish before the code continues, but you don't care about the order the work within the loop finishes, then calling Join is the answer. To add more detail to Kevin Kenny's answer, you should call Join outside the loop. This means you will need a collection to hold references to the threads you started:

// Start all of the threads.
List<Thread> startedThreads = new List<Thread>();
foreach (...) {
Thread thread = new Thread(new ThreadStart(MyMethod));
thread.Start();
startedThreads.Add(thread);
}

// Wait for all of the threads to finish.
foreach (Thread thread in startedThreads) {
thread.Join();
}

In contrast, if you called Join inside the loop, the result would basically be the same as not using threads at all. Each iteration of the loop body would create and start a thread but then immediately Join it and wait for it to finish.

If the individual threads produce some result (write a message in a log, for example) then the messages may still appear out of order because there's no coordination between the threads. It is possible to get the threads to output their results in order by coordinating them with a Monitor.

Waiting until threads finish before executing next step

I would go with the Async / Await pattern on this. It gives you excellent flow control and won't lock up your UI.

Here is a great example from MSDN:

Public Class Form1
Public Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim tasks As New List(Of Task)()
tasks.Add(Task.Run(addressof Task1))
tasks.Add(Task.Run(addressof Task2))
Await Task.WhenAll(tasks)

MsgBox("Done!")
End Sub

Private Async Function Task1() As Task 'Takes 5 seconds to complete
'Do some long running operating here. Task.Delay simulates the work, don't use it in your real code
Await Task.Delay(5000)
End Function

Private Async Function Task2() As Task 'Takes 10 seconds to complete
'Do some long running operating here. Task.Delay simulates the work, don't use it in your real code
Await Task.Delay(10000)
End Function
End Class

The basic idea is to create an array of Task (these can point to functions that return Task also). This queues up the "threads" wrapped in task objects that get run when you call Task.WhenAll, which will execute all the tasks in the array and continue after they all complete. Code after that will run once every tasks completes, but it won't block the UI thread.

Waiting for and terminating a thread after a given time without blocking in .NET 3.5

If I understood your question correctly, the following algorithm should solve your problem:

  • As before, create a BackgroundWorker to do your background work.

  • In BackgroundWorker_DoWork,

    • create a new thread (let's call it the "third-party thread") to call your third-party library, and then
    • wait for the third-party thread to finish or the timeout to elapse. (*)

That way, your UI won't block, since only the Backgroundworker thread is waiting, not the main thread.

Now about the interesting part: How do you wait for the third-party thread to finish (the step marked with (*))?

My suggestion would be to simply use "loop waiting with sleep", i.e. (pseudo-code, you can use the Stopwatch class for the timeout):

do until (third-party thread has finished or x seconds have elapsed):
Thread.Sleep for 100ms

if third-party thread has not finished:
Abort it // we don't have another choice
else
Process the result

It's not best practice, but it's simple, it gets the job done and you can always replace it with fancy cross-thread-syncronization stuff (which is non-trivial to get right) once you got it all working.

How correctly wait of ends of thread array on c#?

I question the need for 'bare threads' but when you're sure about that, then waiting in a while-loop is wasting CPU time. Threads only have the Join() method available for this:

public void WaitAll()
{
foreach(var thread in _threads)
thread.Join();
}

How to wait for all threads to be finish without blocking the UI in VB.NET?

I have found a solution, thanks to @Jimi (List(Of Task) approach).

Friend Sub Main()
Dim tasks As New List(Of Task)
tasks.Add(New Task(AddressOf LongRunningInitialization1))
tasks.Add(New Task(AddressOf LongRunningInitialization2))
tasks.Add(New Task(AddressOf LongRunningInitialization3))
tasks.Add(New Task(AddressOf LongRunningInitialization4))
tasks.Add(New Task(AddressOf LongRunningInitialization5))
tasks.Add(New Task(AddressOf LongRunningInitialization6))
tasks.Add(New Task(AddressOf LongRunningInitialization7))
' Start all tasks.
tasks.All(
Function(t As Task)
t.Start()
Return True
End Function
)
' Wait until all tasks has been finished.
While tasks.Any(Function(t As Task) Not (t.Status = TaskStatus.Canceled OrElse t.Status = TaskStatus.Faulted OrElse t.Status = TaskStatus.RanToCompletion))
Application.DoEvents()
Sleep(1)
End While
End Sub


Related Topics



Leave a reply



Submit