多线程更新主窗体

时间:2011-05-01 04:24:33

标签: c# multithreading events delegates backgroundworker

我有一个应用程序(frmMain)调用一个类(ThreadBL),它启动2个线程(Thread1,Thread2)。当Thread1执行操作时,我希望能够将更新发送回frmMain,类似的Thread2也会这样做。

这是一些切碎的代码,基本上是它的工作原理。我没有机会测试这个特定的代码是否有效,但当我运行我的原始代码时,我得到一个'跨线程操作无效的线程'错误。

有更好的方法从线程更新frmMain吗?这段代码是否过于详尽且不必要?非常感谢任何反馈。

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 sender, 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();
  }
}

1 个答案:

答案 0 :(得分:1)

调用主线程的最佳方法是调用Control.Invoke(...)或Control.BeginInvoke(...)。前者将阻塞,直到主线程处理呼叫。后者只会在主线程释放时发布要处理的调用。

如果您不希望线程知道Control类型,您可以简单地将Invoke和BeginInvoke调用包装到您自己的界面中,例如IInvoker,并声明您的主Form实现它。将界面作为线程的参数传递,你就可以了。

为了进行线程工作,我建议使用ThreadPool。我会做这样的事情(假设所有方法都在你的主要形式的代码中)。

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
   ...
}