Serhat Ozgel
Serhat Ozgel

Reputation: 23766

How to wait for a thread to finish its work

I have a console application. A class (let's say Worker) does some work in a separate thread and throws an event when it finishes. But this never happens because the execution ends instantly. How can I wait for the thread to finish and handle the event after it throws?

static void Main(string[] args)
{
    Worker worker = new Worker();
    worker.WorkCompleted += PostProcess;
    worker.DoWork();
}

static void PostProcess(object sender, EventArgs e) { // Cannot see this happening }

Edit: Corrected the order of the statements but that was not the problem.

Upvotes: 0

Views: 8922

Answers (8)

peSHIr
peSHIr

Reputation: 6360

This sounds like a good candidate of using the Begin/End asynchronous pattern in your worker. So instead of just a DoWork() method that starts to do the work asynchronously and an event that fires when the work is complete, Worker would include a synchronous Work() method that returns the result(s) of the work, and BeginWork() and EndWork() pair that can be used for asynchronous use.

See http://msdn.microsoft.com/en-us/library/seta58yd(VS.71).aspx for some more information about this. Might be a good fit for your situation?

Upvotes: 0

Mohamede1945
Mohamede1945

Reputation:

I think Application.Run after initializing your thread. and on completion call Application.Exit

Upvotes: 0

krosenvold
krosenvold

Reputation: 77201

Have you tried switching the order of the statements ?

static void Main(string[] args)
{
    Worker worker = new Worker();
    worker.WorkCompleted += PostProcess;
    worker.DoWork();
}

WorkCompleted is an event handler and has to be set up-front. This does not get invoked by the assignment worker.WorkCompleted += PostProcess;

Upvotes: 2

Oliver Friedrich
Oliver Friedrich

Reputation: 9250

You could use the BackgroundWorker class from the .net framework for this.

It does exactly what you want to do. In addition it handles invoking itself so no pain for you with this.

static void Main(string[] args)
{
    BackgroundWorker worker = new BackgroundWorker();

    worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
    worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
    worker.WorkerReportsProgress = true;
    worker.WorkerSupportsCancellation = false;
    worker.DoWork += new DoWorkEventHandler(MyThreadMethod);
    worker.RunWorkerAsync();
    Console.Read();
}

static void MyThreadMethod(object sender, DoWorkEventArgs e)
{
    Console.WriteLine("Worker starts working.");
    for (int i = 1; i <= 100; i++)
    {
        ((BackgroundWorker)sender).ReportProgress(i);
    }
    Console.WriteLine("Worker works fine.");
}

static void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    Console.WriteLine("Worker has finished.");
}

static void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    Console.WriteLine("Worker reports progress {0}", e.ProgressPercentage);
}

Upvotes: 0

Brian B.
Brian B.

Reputation: 1779

Assuming you have no access to the Worker class, just add a flag that indicates when PostProcessing has done and sleep until the flag is set:

static bool isProcessed = false;
static void Main(string[] args)
{
    Worker worker = new Worker();
    worker.WorkCompleted += PostProcess;
    worker.DoWork();
    while(!isProcessed)
    {
      System.Threading.Thread.Sleep(-1);
    }
}

static void PostProcess(object sender, EventArgs e) 
{ 
   // Cannot see this happening 
   isProcessed=true;
}

THis should do the trick, but I can't guarantee that it is robust without more information on your setup.

Upvotes: 0

Michael Burr
Michael Burr

Reputation: 340426

The Worker class should have a method that allows the client to wait for the thread to complete. That method would call the appropriate overload of Thread.Join() to implement the wait. If it doesn't (and you have no way to modify the Worker class), it may have some method to get to the internal Thread object and you can perform the Join() on that object.

If it has neither of those, you'll need to look at (and/or post here) more details of the Worker class interface to see if it has an appropriate method to wait for completion. If it doesn't have one, then you'll need your own EventWaitHandle object that the PostProcess() event handler can signal when it gets invoked.

Upvotes: 0

Greg Beech
Greg Beech

Reputation: 136717

You've got a race condition, in that the work could finish before you register for the event. To avoid the race condition, change the order of the code so you register for the event before starting the work, then it will always be raised, no matter how fast it finishes:

static void Main(string[] args)
{
    Worker worker = new Worker();
    worker.WorkCompleted += PostProcess;
    worker.DoWork();
}

Edit:

OK the question has been modified, so it looks like what you're really asking is how to wait for PostProcess to finish executing. There are a couple of ways to do this, but you'll have to add some more code.

The easiest way is, because events always execute on the same thread as they are raised, is to call Thread.Join on the thread the Worker class creates, e.g. assuming the thread is exposed as a property:

worker.Thread.Join();

(Although to be honest I'd probably keep the Thread private and expose a method called something like WaitForCompletion on the Worker class that calls it).

Alternative methods are:

  1. Have a WaitHandle, probably a ManualResetEvent, in the Worker class which is Set when it completes all its work, and call WaitOne on it.

  2. Have a volatile bool complete field in the Worker class and loop while waiting for it to be set to true, using Thread.Sleep in the loop body (this probably isn't a good solution, but it is feasible).

There are probably other options too, but that's the common ones.

Upvotes: 15

amazedsaint
amazedsaint

Reputation: 7642

Use WaitHandle class members (WaitOne, WaitAny, or WaitAll)

See Details Here In MSDN

Upvotes: 2

Related Questions