Ожидание задачи никогда не завершается, хотя ее состояние переходит в RanToCompletion
Во-первых, извинения - я не могу воспроизвести это поведение в достаточно простом примере приложения. Эта функциональность работала до некоторого рефакторинга вызывающего кода.
Я пытаюсь использовать TaskCompletionSource для оповещения об окончании асинхронной операции (либо длительный процесс завершается, либо тайм-аут может сигнализировать о завершении с помощью TrySetResult()).
Моя проблема в том, что, хотя я вижу, что Задача переходит из "WaitingForActivation" в "RanToCompletion", вызов await никогда не завершается.
В качестве теста я создал продолжение задачи, и это называется IS, и я добавил таймер для отображения состояний задачи:
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
Конечно, с продолжением удара, непосредственно ожидающее Задачу также должно завершиться, нет? Какие внешние (вызывающие) факторы могут быть для такого поведения?
1 ответ
Я уверен, что вызывающий код в какой-то момент вверх по стеку вызовов блокирует задачу, и что этот код выполняется в контексте синхронизации (т. Е. В потоке пользовательского интерфейса или из запроса ASP.NET). Это приведет к тупику, который я полностью объясню в своем блоге. Наиболее правильным решением является замена блокировки (обычно Wait
или же Result
вызов) с await
,
Причина ContinueWith
не заблокирован, потому что он использует текущий TaskScheduler
вместо текущего SynchronizationContext
так что в этом случае он, вероятно, будет работать в пуле потоков. Если мои предположения о блокировке вызывающего кода верны, то ContinueWith
будет также тупик, если вы передадите его TaskScheduler.FromCurrentSynchronizationContext()
,