【问题标题】:Awaiting a Task never completes even though its state transitions to 'RanToCompletion'即使任务状态转换为“RanToCompletion”,等待任务也永远不会完成
【发布时间】:2015-10-28 10:51:54
【问题描述】:

首先,抱歉——我无法在适当简单的示例应用程序中重现此行为。此功能在对调用代码进行一些重构之前有效。

我正在尝试使用 TaskCompletionSource 来发出异步操作结束的信号(长时间运行的进程完成,或者超时可能会使用 TrySetResult() 发出完成的信号)。

我的问题是,即使我可以看到 Task 正在从“WaitingForActivation”转换为“RanToCompletion”,但对 await 的调用永远不会完成。

作为测试,我创建了一个 Task Continuation,这个 IS 被调用,我添加了一个 Timer 来显示 Task 状态:

async Task<Foo> WaitForResultOrTimeoutAsync()
{
        //... [Create 'pendingReq' with its TaskCompletion property]

        TaskCompletionSource<Foo> myCompletion = pendingReq.TaskCompletion;

        Task<Foo> theTask = myCompletion.Task;

        var taskContinuation = theTask.ContinueWith(resp =>
        {
            Console.WriteLine("The task completed");
            return resp.Result;
        });

        new Timer(state =>
        {
            Console.WriteLine("theTask TaskCompletion state is {0}", theTask.Status);
            Console.WriteLine("taskContinuation TaskCompletion state is {0}", taskContinuation.Status);
        }, null, 0, 1000);

        //var result = await theTask;
        var result = await taskContinuation;
        Console.WriteLine("We're FINISHED");    // NEVER GETS HERE
        return result;
}

这会产生以下输出:

theTask TaskCompletion state is WaitingForActivation
taskContinuation TaskCompletion state is WaitingForActivation
theTask TaskCompletion state is WaitingForActivation
taskContinuation TaskCompletion state is WaitingForActivation
The task completed
theTask TaskCompletion state is RanToCompletion
taskContinuation TaskCompletion state is RanToCompletion
theTask TaskCompletion state is RanToCompletion
taskContinuation TaskCompletion state is RanToCompletion

当然随着继续被击中,直接等待任务也应该完成,不是吗?这种行为可能有哪些外部(召唤)因素?

【问题讨论】:

  • 检查是否SynchronizationContext.Current != null
  • 您能否编辑您的代码,以便我们可以运行它来复制您的问题?
  • 谢谢@i3arnon -- 这确实是空的,它为我指明了正确的轨道;
  • @Ive 你的意思不是null,对吧?
  • @i3arnon 在等待任何东西之前,它被设置为 WindowsFormsSynchronizationContext。我指的是在延续中,它是空的。我怀疑这是由于斯蒂芬关于使用 TaskScheduler 继续进行的注释......但没有进一步调查。

标签: c# async-await taskcompletionsource


【解决方案1】:

我很确定调用堆栈中某个位置的调用代码阻塞了任务,并且该代码在同步上下文中执行(即,在 UI 线程上或来自 ASP.NET 请求) .这将cause a deadlock 我在我的博客上详细解释。最正确的解决方案是将阻塞(通常是WaitResult 调用)替换为await

ContinueWith 没有被阻塞的原因是因为它使用当前的TaskScheduler 而不是当前的SynchronizationContext,所以在这种情况下它可能最终运行在线程池上。如果我对调用代码阻塞的猜测是正确的,那么 ContinueWith 如果你传递给它 TaskScheduler.FromCurrentSynchronizationContext() 也会死锁。

【讨论】:

  • 是的,你说的很对。在调用堆栈的更远处,我正在处理从同步调用一些异步代码,并引用了一个 Task.Result 来这样做(我知道——淘气)。结合使用 WinForms 应用程序的 Page_Load 事件来测试此组件导致死锁,如您的博客中所述。我随后一直使用 async 回到原来的调用者,问题就解决了。在死锁的等待上设置 ConfigureAwait(false) 也起到了作用。
猜你喜欢
  • 2014-07-19
  • 1970-01-01
  • 2015-02-02
  • 2015-11-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多