简体   繁体   English

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

[英]Two Threads for One Background Process?

I've got to call a routine from a supplied DLL. 我必须从提供的DLL中调用例程。 This DLL takes 2 to 10 minutes to run, depending on the speed of the PC it is being run on. 此DLL需要2到10分钟才能运行,具体取决于运行它的PC的速度。

I have put the DLL call into a BackgroundWorker thread so that the interface will remain responsive. 我已将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;
}

This works, but I can't call this method inline with other calls that are waiting on its results because it will immediately return a null value. 这是有效的,但我不能将此方法与其他等待其结果的调用内联调用,因为它会立即返回空值。

So, I stuck in a ManualResetEvent instance to try and get the method to wait until it actually had a value before returning: 所以,我坚持使用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;
}

However, it appears that doing this causes all of my CPU time to be spent on the ManualResetEvent's WaitOne method and the Set() trigger is never called. 但是,看起来这样做会导致我的所有CPU时间花在ManualResetEvent的WaitOne方法上,并且永远不会调用Set()触发器。

Has anyone encountered this behavior before and found a good workaround for it? 有没有人遇到过这种行为,并找到了一个很好的解决方法呢?

I have created a workaround for it in the past, but it involved creating a second thread to run the WaitOne method so that the first thread can process the DoWork code. 我在过去为它创建了一个解决方法,但它涉及创建第二个线程来运行WaitOne方法,以便第一个线程可以处理DoWork代码。

This works, but I can't call this method inline with other calls that are waiting on its results because it will immediately return a null value. 这是有效的,但我不能将此方法与其他等待其结果的调用内联调用,因为它会立即返回空值。

That is why the async and await keywords were invented. 这就是发明asyncawait关键字的原因。 There is no easy solution for this that doesn't block the thread. 没有简单的解决方案,不会阻止线程。

Since the DLL you are using apparently does not supply methods implemented using any of the Asynchronous Programming Patterns (like BeginOperation / EndOperation , or Task -Based Async Programming), you're stuck with a separate worker thread. 由于您使用的DLL显然不提供使用任何异步编程模式(如BeginOperation / EndOperation或基于任务的异步编程)实现的方法,因此您将遇到单独的工作线程。

What you can do is: 你能做的是:

Start your BackgroundWorker or Thread as usual, then return immediately. 像往常一样启动BackgroundWorkerThread ,然后立即返回。 Do not continue with the operation which would have required the return value of the lengthy DLL process. 不要继续执行需要冗长DLL进程的返回值的操作。 Once the BackgroundWorker or Thread is finished, have it report progress, and in the ProgressChanged event handler, you can retrieve the result of the lengthy DLL process and continue with the operation. 完成BackgroundWorkerThread后,让它报告进度,并在ProgressChanged事件处理程序中,您可以检索冗长的DLL进程的结果并继续操作。 Alternatively you can use the RunWorkerCompleted event (might actually be a better choice). 或者,您可以使用RunWorkerCompleted事件(实际上可能是更好的选择)。

In the meantime, you may have to disable all controls which could start the lengthy DLL process again, or which would otherwise issue invalid operations while the process is running. 在此期间,您可能必须禁用所有可能再次启动冗长DLL进程的控件,否则会在进程运行时发出无效操作。 And as Henk Holterman wrote, do not dispose the BackgroundWorker like that. 正如Henk Holterman写的那样,不要像这样处理BackgroundWorker

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM