I have written a class that uses a worker thread and it utilises an event object. I have cut the class down to the basics:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Threading;
using System.Windows.Forms;
namespace TEST
{
public class TEST_Worker
{
public static ManualResetEvent m_Event = new ManualResetEvent(false);
private BackgroundWorker m_backgroundWorker;
public TEST_Worker()
{
InitBackgroundWorker();
}
private void InitBackgroundWorker()
{
m_backgroundWorker = new BackgroundWorker();
m_backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
m_backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker_ProgressChanged);
m_backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker_RunWorkerCompleted);
m_backgroundWorker.WorkerReportsProgress = true;
}
public void start()
{
m_Event.Reset();
m_backgroundWorker.RunWorkerAsync();
}
public void stop()
{
m_backgroundWorker.CancelAsync();
}
public void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
m_backgroundWorker.ReportProgress(100, "Progress {0}%");
}
// This event handler updates the UI
private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
m_Event.Set();
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// dispose managed resources
m_backgroundWorker.Dispose();
}
// free native resources
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}
In the form class (a menu item handler) I am doing this:
private void testToolStripMenuItem_Click(object sender, EventArgs e)
{
TEST.TEST_Worker myTest = new TEST.TEST_Worker();
myTest.start();
TEST.TEST_Worker.m_Event.WaitOne();
MessageBox.Show("Complete!");
}
I don't have it right. The form needs to not sleep or anything (else the GUI will not get updated - real code does progress bar updates). But, I also want the form to know when the thread has finished and then perform a task. At the moment it just carries on after the call to start
.
Clearly I am not using the ManualResetEvent
in the right way. At the moment my application just hangs indefinitely. It never shows the message box (I don't want the thread to show it - but the form).
Does this make sense? Am I going about this the wrong way?
I have used it before, but admittedly it was when a worker thread was required to wait for a sub-worker thread to finish before it carried on. This context is different, a form calling a worker thread and then wanting to do something when the thread has finished (but not choke the form for GUI updates).
As stated in comment, you may want to try raising custom event from class that wraps Background Worker:
public class TEST_Worker
{
public event EventHandler OnCompleted = delegate { };
// ....
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
OnCompleted(this, EventArgs.Empty);
}
}
// In form:
private void testToolStripMenuItem_Click(object sender, EventArgs e)
{
TEST.TEST_Worker myTest = new TEST.TEST_Worker();
myTest.OnCompleted += (_sender, _e) => {
MessageBox.Show("Complete!");
};
myTest.start();
}
The other way around is to use raw thread and sync for access form via Invoke as described here: Accessing a form's control from a separate thread