How to wait for a thread to finish its work
I have a console application. A class (say Worker) does some work on a separate thread and throws an event when it ends. But this never happens, because execution ends instantly. How can I wait for the thread to end and handle the event after it is thrown?
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: Fixed the order of the operators, but that's not a problem.
source to share
You have a race condition where work may end before you register for the event. To avoid a race condition, change the order of your code so that you register for the event before starting, then it will always be raised no matter how quickly it ends:
static void Main(string[] args)
{
Worker worker = new Worker();
worker.WorkCompleted += PostProcess;
worker.DoWork();
}
Edit:
OK the question has been changed, so it looks like you are really asking how to wait for completion PostProcess
. There are several ways to do this, but you have to add a few more codes.
The easiest way is that events are always executed on the same thread in which they were raised - this is called Thread.Join
on the thread that creates the class Worker
, for example. if the stream is expanded as a property:
worker.Thread.Join();
(Though, to be honest, I would probably keep it Thread
private and expose the method named WaitForCompletion
in the class Worker
that calls it).
Alternative methods:
-
You have
WaitHandle
, perhaps,ManualResetEvent
in a classWorker
that isSet
when it finishes all its work and calls on itWaitOne
. -
You have a field
volatile bool complete
in a class and a loopWorker
, expecting it to be set totrue
usingThread.Sleep
in the body of the loop (this is probably not a good solution, but it is possible).
Perhaps there are other options, but general ones.
source to share
Have you tried switching the order of statements?
static void Main(string[] args)
{
Worker worker = new Worker();
worker.WorkCompleted += PostProcess;
worker.DoWork();
}
WorkCompleted is an event handler and must be configured ahead of time. It will not be triggered by a work assignment. WorkCompleted + = PostProcess;
source to share
The Worker class must have a method that allows the client to wait for the thread to complete. This method will call the appropriate overload Thread.Join()
to implement the wait. If it doesn't (and you can't change the class Worker
), it might have some method to access the inner Thread object, and you can do a Join () on that object.
If it does not have any of these, you will need to look (and / or post here) more details on the class interface Worker
to see if it has a suitable method to wait for completion. If it doesn't have one, then you need your own object EventWaitHandle
that the event handler PostProcess()
can signal when it is called.
source to share
Assuming you don't have access to the Worker class, just add a flag that indicates when PostProcessing has completed and hibernate 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 cannot guarantee that it is reliable without further information on your setup.
source to share
You can use a class BackgroundWorker
from the .net framework for this .
It does exactly what you want it to do. Plus, it handles the call, so you don't get hurt with it.
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);
}
source to share
This sounds like a good candidate for your worker's Begin / End asynchronous pattern. Therefore, instead of a method DoWork()
that starts working asynchronously and an event that fires when the work is complete, it Worker
will include a synchronous method Work()
that returns the result of the work, and BeginWork()
and EndWork()
, which can be used for asynchronous use.
See http://msdn.microsoft.com/en-us/library/seta58yd(VS.71).aspx for more information on this. Maybe this works well for your situation?
source to share