Multi-Threads to update Main Form
I have an application (frmMain) that calls a class (ThreadBL) which starts 2 threads (Thread1, Thread2). When Thread1 performs operations I want to be able to send updates back through to frmMain, similarly Thread2 would do the same.
Here is some chopped up code which is basically how it works. I havent had a chance to to test if this specific code works, but when I run what I have the original code I get a 'Cross-thread operation not valid with threading' error.
Is there a better way of updating frmMain from the threads? Is this code too exhaustive and unnecessary? Any feedback is greatly appreciated.
public class ThreadExample() {
private void ThreadExample() {};
public delegate void CurrentFileProcessing(string filename);
public event CurrentFileProcessing CurrentFileProcessingEvent;
public bool startCopying() {
CurrentFileProcessingEvent += new CurrentFileProcessing(handlerCurrentFileProcessing);
copyFiles();
return true;
}
public void copyFiles() {
CurrentFileProcessingEvent("Copying: file.xml");
}
private void handlerCurrentFileProcessing(string filename) {
Console.WriteLine("Processing: " + filename);
}
}
public class ThreadBL() {
private void ThreadBL() {};
public delegate void Thread1CurrentProcessing(string filename);
public delegate void Thread2CurrentProcessing(string filename);
public event Thread1CurrentProcessing Thread1CurrentProcessingEvent;
public event Thread2CurrentProcessing Thread2CurrentProcessingEvent;
private bool processingThread1 = false;
private bool processingThread2 = false;
public void processThreads() {
BackgroundWorker thread1BW = new BackgroundWorker();
thread1BW.DoWork += new DoWorkEventHandler(thread1Process);
thread1BW.RunWorkerCompleted += new RunWorkerCompletedEventHandler(completeThread1);
thread1BW.RunWorkerAsync();
while (!processingThread1) {
Console.WriteLine("Waiting for thread1 to finish. TID: " + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(100);
}
BackgroundWorker thread2BW = new BackgroundWorker();
thread2BW.DoWork += new DoWorkEventHandler(thread2Process);
thread2BW.RunWorkerCompleted += new RunWorkerCompletedEventHandler(completeThread2);
thread2BW.RunWorkerAsync();
while (!thread2Done) {
Console.WriteLine("Waiting for thread2 to finish. TID: " + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(100);
}
}
private void thread1Process() {
ThreadExample thread1Example = new ThreadExample();
thread1Example.CurrentFileProcessingEvent += new ThreadExample.CurrentFileProcessing(handlerThread1CurrentProcessingEvent);
processingThread1 = thread1Example.startCopying();
}
private void completeThread1(object se开发者_如何学Cnder, RunWorkerCompletedEventArgs e) {
Console.WriteLine("Completed Thread1. TID: " + Thread.CurrentThread.ManagedThreadId);
processingThread1 = true;
}
private void thread2Process() {
ThreadExample thread2Example = new ThreadExample();
thread2Example.CurrentFileProcessingEvent += new ThreadExample.CurrentFileProcessing(handlerThread2CurrentProcessingEvent);
processingThread2 = thread2Example.startCopying();
}
private void completeThread2(object sender, RunWorkerCompletedEventArgs e) {
Console.WriteLine("Completed Thread1. TID: " + Thread.CurrentThread.ManagedThreadId);
processingThread2 = true;
}
private void handlerThread2CurrentProcessingEvent(string filename) {
Console.WriteLine("Thread2 Processing: " + filename);
Thread2CurrentProcessingEvent(filename);
}
}
public class frmMain {
private ThreadBL threadBL = new ThreadBL();
public void frmMain() {
threadBL.Thread1CurrentProcessingEvent += new ThreadExample.CurrentFileProcessing(handlerThread1ProgressEvent);
threadBL.Thread2CurrentProcessingEvent += new ThreadExample.CurrentFileProcessing(handlerThread2ProgressEvent);
threadBL.processThreads();
}
private void handlerThread1ProgressEvent(string progress) {
lblCopyingProgress.Invoke(new MethodInvoker(delegate { lblCopyingProgress.Text = progress; }));
this.Refresh();
}
private void handlerThread2ProgressEvent(string progress) {
lblCopyingProgress.Invoke(new MethodInvoker(delegate { lblCopyingProgress.Text = progress; }));
this.Refresh();
}
}
The best way to call on the main thread is to call Control.Invoke(...) or Control.BeginInvoke(...). The former will block until the main thread processes the call. The latter will simply post the call to be processed when the main thread frees up.
If you don't want your threads aware of the Control type, you can simply wrap the Invoke and BeginInvoke calls into your own interface, say, IInvoker, and declare your main Form as implementing it. Pass the interface as your thread's param and you're good to go.
For doing thread work, I recommend using the ThreadPool. I'd do something like this (assume all methods are in your main form's code).
private void MyThread(object param)
{
MyForm form = (MyForm) param; // pass your form as your param
DoWork(); // Whatever it is you are doing on your thread
form.Invoke(new MethodInvoker(form.NotifyComplete)); // Invokes on main thread
}
public void Button_OnClick(object sender, EventArgs args)
{
ThreadPool.QueueUserWorkItem(new Action<object>(MyThread), this);
}
private void NotifyComplete()
{
// update your controls here
...
}
精彩评论