Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/search/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 当线程使用调度程序且主线程正在等待线程完成时发生死锁_C#_Wpf_Multithreading_Deadlock_Dispatcher - Fatal编程技术网

C# 当线程使用调度程序且主线程正在等待线程完成时发生死锁

C# 当线程使用调度程序且主线程正在等待线程完成时发生死锁,c#,wpf,multithreading,deadlock,dispatcher,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 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
解释:我需要我的辅助线程来计算结果,并将其返回到主线程。但是次线程还必须将调试信息写入日志;并且日志位于wpf窗口中,因此线程需要能够使用dispatcher.invoke()。但是当我执行Dispatcher.Invoke时,就会发生死锁,因为主线程正在等待次线程完成,因为它需要结果

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

另外,理论上,我不理解一件事:死锁只能在两个线程以不同顺序访问两个共享资源时发生。但在这种情况下,实际的资源是什么?一个是GUI。但另一个呢?我看不见


死锁通常是通过强制执行一条规则来解决的,即线程只能以精确的顺序锁定资源。我已经在其他地方做过了。但是在这种情况下,既然我不知道实际的资源是什么,我怎么能强加这个规则呢?

发生这种死锁是因为UI线程正在等待后台线程完成,而后台线程正在等待UI线程释放

最好的解决方案是使用
async

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

Dispatcher
正试图在UI消息循环中执行工作,但同一个循环当前卡在
th.Join
上,因此它们彼此等待,从而导致死锁

如果你启动了一个
线程
,并立即
加入它,你肯定会有一股代码味道,应该重新思考你在做什么


如果您希望在不阻塞UI的情况下完成操作,您可以在
InvokeAsync

上简单地
wait
简单回答:使用
BeginInvoke()
而不是
Invoke()
。 长答案改变你的方法:看备选答案

当前,您的
线程.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;
}
void DoSomehtingCool()
{
var factory=new TaskFactory(TaskScheduler.FromCurrentSynchronizationContext());
factory.StartNew(()=>
{
var结果=等待强度计算();
AppendText(“计算结果:”+结果);
});
}
异步任务强度计算()
{
睡眠(5000);
返回20;
}

我遇到了一个类似的问题,我最终以这种方式解决了这个问题:

do{
    // Force the dispatcher to run the queued operations 
    Dispatcher.CurrentDispatcher.Invoke(delegate { }, DispatcherPriority.ContextIdle);
}while(!otherthread.Join(1));
这将生成一个连接,该连接不会因为另一个线程上的GUI操作而阻塞

这里的主要技巧是使用空委托(无操作)阻塞
调用
,但优先级设置小于队列中的所有其他项。这将强制调度器处理整个队列。(默认优先级为
DispatcherPriority.Normal=9
,因此我的
DispatcherPriority.ContextIdle=3
的优先级很低。)


Join()调用使用1毫秒的超时,只要连接不成功,就会重新清空调度程序队列。

我非常喜欢@user5770690 answer。我创建了一个扩展方法,该方法可以保证调度器中的持续“泵送”或处理,并避免此类死锁。我稍微改变了一下,但效果很好。我希望它能帮助别人

    public static Task PumpInvokeAsync(this Dispatcher dispatcher, Delegate action, params object[] args)
    {
        var completer = new TaskCompletionSource<bool>();

        // exit if we don't have a valid dispatcher
        if (dispatcher == null || dispatcher.HasShutdownStarted || dispatcher.HasShutdownFinished)
        {
            completer.TrySetResult(true);
            return completer.Task;
        }

        var threadFinished = new ManualResetEvent(false);
        ThreadPool.QueueUserWorkItem(async (o) =>
        {
            await dispatcher?.InvokeAsync(() =>
            {
                action.DynamicInvoke(o as object[]);
            });
            threadFinished.Set();
            completer.TrySetResult(true);
        }, args);

        // The pumping of queued operations begins here.
        do
        {
            // Error condition checking
            if (dispatcher == null || dispatcher.HasShutdownStarted || dispatcher.HasShutdownFinished)
                break;

            try
            {
                // Force the processing of the queue by pumping a new message at lower priority
                dispatcher.Invoke(() => { }, DispatcherPriority.ContextIdle);
            }
            catch
            {
                break;
            }
        }
        while (threadFinished.WaitOne(1) == false);

        threadFinished.Dispose();
        threadFinished = null;
        return completer.Task;
    }
公共静态任务PumpInvokeAsync(此调度程序、委托操作、参数对象[]args)
{
var completer=new TaskCompletionSource();
//如果没有有效的调度程序,请退出
if(dispatcher==null | | | | dispatcher.hashutdownstarted | | | dispatcher.hashutdownFinished)
{
完成者。TrySetResult(真);
返回完成任务;
}
var threadFinished=新手动重置事件(错误);
ThreadPool.QueueUserWorkItem(异步(o)=>
{
等待调度程序?.InvokeAsync(()=>
{
action.DynamicInvoke(o作为对象[]);
});
threadFinished.Set();
完成者。TrySetResult(真);
},args);
//排队操作的泵送从这里开始。
做
{
//错误条件检查
if(dispatcher==null | | | | dispatcher.hashutdownstarted | | | dispatcher.hashutdownFinished)
打破
尝试
{
//通过以较低优先级泵送新消息来强制处理队列
Invoke(()=>{},DispatcherPriority.ContextIdle);
}
抓住
{
打破
}
}
while(threadFinished.WaitOne(1)=false);
threadFinished.Dispose();
threadFinished=null;
返回完成任务;
}

这就是为什么你不应该阻止UI线程。另一个资源是
th.Join()
中的
th
。我甚至不确定在这种情况下是否需要一个新线程all@YuvalItzchakov:他大概(希望如此)不想阻止用户界面。不;后台线程中的内容不应该阻塞UI。谢谢,非常有趣。我不熟悉wait和InvokeAsync,所以我现在还不清楚这是否有效。我不会