3

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

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

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

作为测试,我创建了一个 Task Continuation,它被调用,我添加了一个 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

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

4

1 回答 1

3

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

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

于 2015-10-28T12:15:20.637 回答