0

我必须从提供的 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代码。

4

1 回答 1

2

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

这就是发明asyncandawait关键字的原因。没有简单的解决方案不会阻塞线程。

由于您使用的 DLL 显然不提供使用任何异步编程模式(如BeginOperation/EndOperation或基于任务的异步编程)实现的方法,因此您被困在单独的工作线程中。

你可以做的是:

Start your BackgroundWorker or Thread as usual, then return immediately. Do not continue with the operation which would have required the return value of the lengthy DLL process. 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. Alternatively you can use the RunWorkerCompleted event (might actually be a better choice).

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. And as Henk Holterman wrote, do not dispose the BackgroundWorker like that.

于 2012-10-03T14:39:30.097 回答