当线程使用调度器并且主线程正在等待线程完成时死锁

seg*_*uso 5 c# wpf multithreading deadlock dispatcher

有人可以解释为什么这会造成僵局,以及如何解决它?

        txtLog.AppendText("We are starting the thread" + Environment.NewLine);

        var th = new Thread(() =>
        {

            Application.Current.Dispatcher.Invoke(new Action(() => // causes deadlock
            {
                txtLog.AppendText("We are inside the thread" + Environment.NewLine); // never gets printed
                // compute some result...
            }));


        });

        th.Start();
        th.Join(); // causes deadlock
        // ... retrieve the result computed by the thread
Run Code Online (Sandbox Code Playgroud)

说明:我需要我的辅助线程来计算结果,并将其返回到主线程。但是辅助线程也必须将调试信息写入日志;并且日志在 wpf 窗口中,因此线程需要能够使用 dispatcher.invoke()。但是在我执行 Dispatcher.Invoke 的那一刻,发生了死锁,因为主线程正在等待辅助线程完成,因为它需要结果。

我需要一个模式来解决这个问题。请帮我重写这段代码。(请编写实际代码,不要只说“使用 BeginInvoke”)。谢谢你。

另外,理论上,我不明白一件事:只有当两个线程以不同的顺序访问两个共享资源时,才会发生死锁。但在这种情况下,实际资源是什么?一种是图形用户界面。但另一个是什么?我看不到。

而死锁通常是通过强加线程只能以精确的顺​​序锁定资源的规则来解决的。我已经在其他地方这样做了。但是在这种情况下我怎么能强加这个规则,因为我不明白实际的资源是什么?

Jai*_*ero 6

简短回答:使用BeginInvoke()而不是Invoke(). 长答案改变你的方法:见替代方案。

目前您Thread.Join()正在导致主线程被阻塞,等待辅助线程终止,但辅助线程正在等待主线程执行您的 AppendText 操作,因此您的应用程序已死锁。

如果您更改为,BeginInvoke()那么您的辅助线程将不会等到主线程执行您的操作。取而代之的是,它将对您的调用进行排队并继续。您的主线程不会在 Join() 上阻塞,因为这次您的辅助线程成功结束。然后,当主线程完成时,此方法将自由处理对 AppendText 的排队调用

选择:

void DoSomehtingCool()
{
    var factory = new TaskFactory(TaskScheduler.FromCurrentSynchronizationContext());
    factory.StartNew(() =>
    {
        var result = await IntensiveComputing();
        txtLog.AppendText("Result of the computing: " + result);
    });
}

async Task<double> IntensiveComputing()
{
    Thread.Sleep(5000);
    return 20;
}
Run Code Online (Sandbox Code Playgroud)


SLa*_*aks 2

发生这种死锁的原因是 UI 线程正在等待后台线程完成,而后台线程正在等待 UI 线程空闲。

最好的解决方案是使用async

var result = await Task.Run(() => { 
    ...
    await Dispatcher.InvokeAsync(() => ...);
    ...
    return ...;
});
Run Code Online (Sandbox Code Playgroud)