【问题标题】:Unblock main thread while waiting for another thread to finish it's work在等待另一个线程完成它的工作时取消阻塞主线程
【发布时间】:2014-09-22 16:04:49
【问题描述】:

在 WinForms 应用程序中,我启动了将数据添加到根 XElement 的工作线程。

然后在主线程中我需要等待工作线程完成它的工作(以完成XElement),并将此 XElement 输出到textarea

如果我在主线程上调用.Join() - 它会冻结,直到另一个线程停止(并且用户无法单击主窗体上的任何按钮)。

是否可以在等待另一个线程完成工作时解除阻塞主线程?

我试过了:

1.

BeginInvoke(new Action(() => {
     XmlManager.whReady.WaitOne();
     xDocString = xResultDoc.ToString();
}));

2.

string xDocString = String.Empty;
new Thread(() => { xDocString = XelementToString(); }).Start();

txtXmlTree.Text = xDocString;

public string XelementToString() {
    XmlManager.whReady.WaitOne();
    return xResultDoc.ToString();
}

但是没有效果。

EventWaitHandle XmlManager.whReady.WaitOne(); 在工作线程关闭之前正在成为.Set()

【问题讨论】:

    标签: c# .net multithreading winforms


    【解决方案1】:

    是的,你可以使用 async/await

    string docString = await Task.Run(() => {
     XmlManager.whReady.WaitOne();
     return xResultDoc.ToString();
    }).ConfigureAwait(true);
    
    //Execution flow will resume here once the thread is done.
    ....
    //Now do something here with the text (e.g. display it).
    ...
    

    例如,如果您想在单击按钮时运行它,您将拥有(注意 async 修饰符):

    private async void button1_Click(object sender, EventArgs e) 
    {
      ...The code above goes here...
    }
    

    至于为什么您的代码没有按预期工作(您的两次尝试):

    1. 您的代码处于阻塞状态,因为它会导致在创建主窗体句柄的线程(即 UI 线程)上执行操作。您通常从另一个(非 UI)线程调用 BeginInvoke 来告诉 UI 做某事。
    2. 您启动一个线程,然后立即期望xDocString 可以使用。它不是那样工作的。到执行此行时txtXmlTree.Text = xDocString; 线程可能已完成执行,也可能尚未完成(很可能未完成)。

    我希望你现在明白为什么await 是一个更好的选择。您不必将工作线程与主线程同步,不必担心上下文切换并确保 UI 代码在 UI 线程上执行。

    【讨论】:

    • 您能否实现相同但不使用async/await?因为我对多线程还很陌生而且我还没有学过async/await :(
    • @jamesbong:没有 async/await 的代码太多了。实际上,我写它比你理解这一点需要更多的时间。没有什么可失去的。
    • 可能我夸大了一点,没有那么多代码。但是您仍然应该了解 async 和 await。
    • 你的代码和我的代码一样吗 (#1) 我打电话给BeginInvoke
    • @jamesbong:不,不是真的。您的代码是阻塞的,因为它会导致在创建主窗体句柄的线程上执行操作(因此是 UI 线程)。您通常从 另一个(非 UI)线程调用 BeginInvoke 来告诉 UI 做某事。
    【解决方案2】:

    您可以使用BackgroundWorker 类,因为它是一个 WinForm 应用程序。

    BackgroundWorker 将让子任务在后台运行,并通知主窗体完成(以及进度,如果需要),因此主窗体将能够在文本中显示输出子任务完成后的区域。

    【讨论】:

    • 重写整个应用程序为时已晚 :)
    • 无需重写任何东西。就像将 BackgroundWorker 放到表单中并附加到它的 DoWorkRunWorkerFinished 事件一样简单。阅读链接的 MSDN 页面,它有一些非常完整的示例。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多