Задача не переходит в ошибочное состояние, если для ConfigurAwait установлено значение False

Вот что я пытаюсь достичь. Я запускаю задачу и не делаю ожидания / результата. Чтобы убедиться, что если запущенная задача перейдет в состояние сбоя (например, сгенерировать исключение), я завершу процесс, вызвав Environment FailFast в Continuation.

Как проблема, с которой я сталкиваюсь, заключается в том, что если я запускаю ниже кода, Inside ContinueWith, состояние задачи (которая вызвала исключение) отображается как "RanToCompletion". Я ожидал, что это будет ошибочное состояние.

    private Task KickOfTaskWorkAsync()
    {
        var createdTask = Task.Run(() => this.RunTestTaskAsync(CancellationToken.None).ConfigureAwait(false), CancellationToken.None);

        createdTask.ContinueWith(
            task => Console.WriteLine("Task State In Continue with => {0}", task.Status));

        return createdTask;
    }

    private async Task RunTestTaskAsync(CancellationToken cancellationToken)
    {
        throw new Exception("CrashingRoutine: Crashing by Design");
    }

Это действительно странно:(Если я удаляю "ConfigureAwait(false)" из вызова функции Task.Run, задача переходит в состояние Failed внутри Продолжить с. Действительно в растерянности, чтобы объяснить, что происходит, и была бы признательна за помощь сообщества.

[Обновление]: Мой коллега указал на очевидную ошибку. Я использую ConfigureAwait, в то время как я делаю вызов RunTestAsync внутри Test.Run, хотя я не жду этого. В этом случае ConfigureAwait не возвращает задачу в Task.Run. Если я не вызываю ConfigureAwait, Задача возвращается, и все работает как положено.

1 ответ

Решение

Ваша ошибка является конкретным примером более широкой категории ошибок: вы не наблюдаете Task вы на самом деле заботитесь.

В вашем примере кода RunTestTaskAsync() возвращает Task объект. Завершается синхронно (потому что нет await), Итак Task объект, который он возвращает, уже сбился при возврате метода из-за исключения. Ваш код тогда звонит ConfigureAwait() на этом вина Task объект.

Но все это происходит внутри другого Taskто есть тот, который вы запускаете при звонке Task.Run(), это Task ничего не делает, чтобы соблюсти исключение, поэтому оно завершается нормально.

Причина, по которой вы наблюдаете исключение при удалении ConfigureAwait() Вызов не имеет ничего общего с самим вызовом. Если вы оставили вызов и прошли true вместо этого вы все равно не заметите исключение. Причина, по которой вы можете наблюдать исключение при удалении вызова, заключается в том, что без вызова ConfigureAwait()возвращаемое значение лямбда-выражения Taskи это вызывает другую перегрузкуTask.Run(),

Эта перегрузка немного отличается от других. Из документации:

Поставляет в очередь указанную работу для запуска в пуле потоков и возвращает прокси для задачи, возвращаемой функцией.

То есть пока начинается новый Task, Task объект, который он возвращает, представляет не Task, но тот, который возвращается вашим лямбда-выражением. И этот прокси принимает то же состояние, что и Task это оборачивает, так что вы видите это в Faulted государство.

Исходя из кода, который вы разместили, я бы сказал, что вам не следует звонить Task.Run() на первом месте. Следующее будет работать так же хорошо, без издержек и усложнения прокси:

static void Main(string[] args)
{
    Task createdTask = RunTestTaskAsync();

    createdTask.ConfigureAwait(false);

    createdTask.ContinueWith(
        task => Console.WriteLine("Task State In Continue with => {0}", task.Status)).Wait();
}

private static async Task RunTestTaskAsync()
{
    throw new Exception("CrashingRoutine: Crashing by Design");
}

(Я удалил CancellationToken ценности, потому что они не имеют никакого отношения к вашему вопросу и здесь совершенно излишни.)

Другие вопросы по тегам