C#:方法调用永远不会返回

标签 c# multithreading backgroundworker invoke

我有一个永远不会返回的线程调用。

线程运行良好,直到我以这种方式调用行,“owner.Invoke(methInvoker);

调试时,我可以慢慢地一步一步,一步一步,但是一旦我点击owner.Invoke...就结束了!

Control owner;
public event ReportCeProgressDelegate ProgressChanged;

public void ReportProgress(int step, object data) {
  if ((owner != null) && (ProgressChanged != null)) {
    if (!CancellationPending) {
      ThreadEventArg e = new ThreadEventArg(step, data);
      if (owner.InvokeRequired) {
        MethodInvoker methInvoker = delegate { ProgressChanged(this, e); };
        owner.Invoke(methInvoker);
      } else {
        ProgressChanged(this, e);
      }
    } else {
      mreReporter.Set();
      mreReporter.Close();
    }
  }
}

仅供引用:这是一个模仿 BackgroundWorker 类的自定义类,它在没有 Forms 的控件上不可用。

考虑到可能不需要 Invoke,我在调试器中手动将光标移到该部分代码上并尝试直接调用 ProgressChanged,但 VS2010 的调试器抛出了跨线程异常。

编辑:

由于我收到了前 3 条评论,我想使用我的 ProgressChanged 方法进行更新:

worker.ProgressChanged += delegate(object sender, ThreadEventArg e) {
  if (progressBar1.Style != ProgressBarStyle.Continuous) {
    progressBar1.Value = 0;
    object data = e.Data;
    if (data != null) {
      progressBar1.Maximum = 100;
    }
    progressBar1.Style = ProgressBarStyle.Continuous;
  }
  progressBar1.Value = e.ProgressPercentage;
};

匿名方法的第一行有一个断点,但它也从未被命中。

编辑 2

下面是对线程调用的更完整列表:

List<TableData> tList = CollectTablesFromForm();
if (0 < tList.Count) {
  using (SqlCeReporter worker = new SqlCeReporter(this)) {
    for (int i = 0; i < tList.Count; i++) {
      ManualResetEvent mre = new ManualResetEvent(false);
      worker.StartThread += SqlCeClass.SaveSqlCeDataTable;
      worker.ProgressChanged += delegate(object sender, ThreadEventArg e) {
        if (progressBar1.Style != ProgressBarStyle.Continuous) {
          progressBar1.Value = 0;
          object data = e.Data;
          if (data != null) {
            progressBar1.Maximum = 100;
          }
          progressBar1.Style = ProgressBarStyle.Continuous;
        }
        progressBar1.Value = e.ProgressPercentage;
      };
      worker.ThreadCompleted += delegate(object sender, ThreadResultArg e) {
        Cursor = Cursors.Default;
        progressBar1.Visible = false;
        progressBar1.Style = ProgressBarStyle.Blocks;
        if (e.Error == null) {
          if (e.Cancelled) {
            MessageBox.Show(this, "Save Action was Cancelled.", "Save Table " + tList[i].TableName);
          }
        } else {
          MessageBox.Show(this, e.Error.Message, "Error Saving Table " + tList[i].TableName, MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
        mre.Set();
      };
      worker.RunWorkerAsync(tList[i]);
      progressBar1.Value = 0;
      progressBar1.Style = ProgressBarStyle.Marquee;
      progressBar1.Visible = true;
      Cursor = Cursors.WaitCursor;
      mre.WaitOne();
    }
  }
}

我希望这不是矫枉过正!我讨厌提供太多信息,因为这样我就会让人们批评我的风格。 :)

最佳答案

  worker.RunWorkerAsync(tList[i]);
  //...
  mre.WaitOne();

这是一个有保证的死锁。您传递给 Control.Begin/Invoke() 的委托(delegate)只能在 UI 线程空闲时运行,重新进入消息循环。您的 UI 线程没有空闲,它在 WaitOne() 调用中被阻塞。在您的工作线程完成之前,该调用无法完成。在 Invoke() 调用完成之前,您的工作线程无法完成。在 UI 线程空闲之前,该调用无法完成。死锁城。

阻塞 UI 线程从根本上说是错误的做法。不仅仅是因为 .NET 管道,COM 已经要求它永不阻塞。这就是为什么 BGW 有一个 RunWorkerCompleted 事件。

关于C#:方法调用永远不会返回,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6549938/

相关文章:

android - 为什么AsyncTask的doInBackground()中不能使用Context

.net - BackgroundWorker 是否提供真正的多线程?

c# - 使用Ping映射事件IP的线程问题-C#

c# - 如何从 Windows 窗体将单个文件发布到 asp.net webform?

c# - 用于演示项目的 .NET 托管

c# - 关于从 Parallel.ForEach() 中调用外部方法是否有任何特殊规则或注意事项?

c# - BackgroundWorker 报告来自外部类的进度?

C#:IEnumerable<T>.HasDuplicates 的良好且高效的实现

c# - 使用 NInject Mocking Kernel 模拟返回接口(interface)的方法

python - 为什么我的 Python 程序的输出看起来像这样(很奇怪)?