Многопоточность для обновления главной формы - PullRequest
2 голосов
/ 01 мая 2011

У меня есть приложение (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 Ответ

1 голос
/ 01 мая 2011

Лучший способ вызвать основной поток - это вызвать Control.Invoke (...) или Control.BeginInvoke (...).Первый будет блокироваться, пока основной поток не обработает вызов.Последний просто отправит вызов для обработки, когда основной поток освободится.

Если вы не хотите, чтобы ваши потоки знали о типе Control, вы можете просто обернуть вызовы Invoke и BeginInvoke в свой собственный интерфейсскажем, IInvoker, и объявите вашу основную форму как реализующую ее.Передайте интерфейс как параметр вашего потока, и все готово.

Для работы с потоками я рекомендую использовать 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
   ...
}
...