一个后台进程的两个线程?

时间:2012-10-03 14:26:02

标签: c# multithreading backgroundworker

我必须从提供的DLL调用例程。此DLL需要2到10分钟才能运行,具体取决于运行它的PC的速度。

我已将DLL调用放入BackgroundWorker线程中,以便界面保持响应。

private object Load(string feature) {
  object result = null;
  using (var w = new BackgroundWorker()) {
    w.WorkerReportsProgress = true;
    w.WorkerSupportsCancellation = true;
    w.DoWork += delegate(object sender, DoWorkEventArgs e) {
      e.Result = DAL.get(feature);
    };
    w.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e) {
      progressBar1.Visible = false;
      if (e.Error != null) {
        MessageBox.Show(this, e.Error.Message, "Load Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
      } else {
        result = e.Result;
      }
    };
    w.RunWorkerAsync();
    if (w.IsBusy) {
      progressBar1.Style = ProgressBarStyle.Marquee;
      progressBar1.Visible = true;
    }
  }
  return result;
}

这样可行,但我不能将此方法与其他等待其结果的调用一起调用,因为它会立即返回空值。

所以,我坚持使用ManualResetEvent实例来尝试让方法等到它返回之前实际有一个值:

private object Load(string feature) {
  object result = null;
  using (var w = new BackgroundWorker()) {
    var mre = new ManualResetEvent(false);
    w.WorkerReportsProgress = true;
    w.WorkerSupportsCancellation = true;
    w.DoWork += delegate(object sender, DoWorkEventArgs e) {
      e.Result = DAL.get(feature);
    };
    w.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e) {
      progressBar1.Visible = false;
      if (e.Error != null) {
        MessageBox.Show(this, e.Error.Message, "Model Load Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
      } else {
        result = e.Result;
      }
      mre.Set();
    };
    w.RunWorkerAsync();
    if (w.IsBusy) {
      progressBar1.Style = ProgressBarStyle.Marquee;
      progressBar1.Visible = true;
      progressBar1.Value = 0;
      const string statusRun = @"\|/-";
      const string statusMsg = "Loading Data...";
      int loops = 0;
      do {
        int index = loops++ % 4;
        tsStatus.Text = statusMsg + statusRun[index].ToString(); // '\', '|', '/', '-'
      } while (!mre.WaitOne(200));
    }
  }
  return result;
}

但是,这样做会导致我的所有CPU时间花费在ManualResetEvent的WaitOne方法上,并且永远不会调用Set()触发器。

之前是否有人遇到此行为并找到了一个好的解决方法?

我在过去为它创建了一个解决方法,但它涉及创建第二个线程来运行WaitOne方法,以便第一个线程可以处理DoWork代码。

1 个答案:

答案 0 :(得分:2)

  

这样可行,但我不能将此方法与其他等待其结果的调用一起调用,因为它会立即返回空值。

这就是发明asyncawait个关键字的原因。没有简单的解决方案可以阻止线程。

由于你使用的DLL显然不提供使用任何异步编程模式(如BeginOperation / EndOperation或基于Task的 - 基于异步编程)实现的方法,所以你'坚持使用单独的工作线程。

你能做的是:

像往常一样开始BackgroundWorkerThread,然后立即返回。不要继续执行需要冗长DLL进程的返回值的操作。完成BackgroundWorkerThread后,让它报告进度,并在ProgressChanged事件处理程序中,您可以检索冗长的DLL进程的结果并继续操作。或者,您可以使用RunWorkerCompleted事件(实际上可能是更好的选择)。

与此同时,您可能必须禁用所有可能再次启动冗长DLL进程的控件,否则会在进程运行时发出无效操作。正如亨克霍尔特曼写的那样,不要像那样处置BackgroundWorker