Reputation: 23766
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
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
Reputation:
I think Application.Run after initializing your thread. and on completion call Application.Exit
Upvotes: 0
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
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
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
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
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:
Have a WaitHandle
, probably a ManualResetEvent
, in the Worker
class which is Set
when it completes all its work, and call WaitOne
on it.
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