views:

1057

answers:

8

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.

+2  A: 

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;

krosenvold
Thanks, but it is correct in my real code, that is not the problem.
Serhat Özgel
+2  A: 

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

See Details Here In MSDN

amazedsaint
+13  A: 

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.

Greg Beech
worker.Thread.Join(); is what you are after
Pondidum
A: 

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.

Michael Burr
Worker class has no Join method. What is it supposed to do?
Serhat Özgel
Sorry - I answered as if the worker object was an object of the Thread class. I've amended my answer to be more generic.
Michael Burr
A: 

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.

Brian B.
That is manual code and wastage of CPU to wait for processing to complete. WaitHandles are meant to tackle these situations.
icelava
I actually have no idea what the behavior of WaitHandle is when it is only signaled from the blocking thread, and I was too lazy to test it. I had assume PostProcess executes on the blocked thread. Without more information this was an easy solution that works.
Brian B.
A: 

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);
}
BeowulfOF
A: 

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://is.gd/eXlk for some more information about this. Might be a good fit for your situation?

peSHIr
(Assuming you actually wrote or otherwise have access to the source code of the Worker class, of course.)
peSHIr
A: 

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