等待任务永远不会完成,即使它的状态转换到';RanToCompletion';

本文关键字:状态 转换 RanToCompletion 永远 任务 等待 | 更新日期: 2023-09-27 18:19:32

首先,很抱歉--我无法在一个适当简单的示例应用程序中重现这种行为。在对调用代码进行某种重构之前,此功能就已经工作了。

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

我的问题是,尽管我可以看到Task正在从"WaitingForActivation"转换为"RanToCompletion",但等待的调用永远不会完成。

作为一个测试,我创建了一个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

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

等待任务永远不会完成,即使它的状态转换到';RanToCompletion';

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

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