Означает ли ожидание Task.CompletedTask, что метод asyn c будет работать синхронно? - PullRequest
3 голосов
/ 16 января 2020
static async Task WaitTaskCompleted()
{
    //Use Thread A before await Task.CompletedTask
    await Task.CompletedTask;
    //Will the code after await Task.CompletedTask always use Thread A, or there is chance to have a Thread B?
}

, что означает, что await Task.CompletedTask всегда будет выполнять метод синхронно?

Ответы [ 2 ]

9 голосов
/ 16 января 2020

Да, этот код всегда будет работать синхронно; главный компилятор продолжения goo вызывается только при обнаружении первого неполного ожидаемого.

Это можно увидеть в sharplab - в частности, здесь:

awaiter = Task.CompletedTask.GetAwaiter();
if (!awaiter.IsCompleted)
{
    num = (<>1__state = 0);
    <>u__1 = awaiter;
    <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
    return;
}

Это AwaitUnsafeOnCompleted(...) + return, который реализует асинхронность.

0 голосов
/ 29 января 2020

Просто для здравомыслия других читателей обычная цель Task.FromResult<T>, Task.CompletedTask, Task.FromCancelation и Task.FromException() - предоставить простые фабричные методы для различных типов Task (то есть с / без возвратной полезной нагрузки или для возврата исключения или отмены mimi c), и во всех случаях возвращенные задачи будут рассматриваться как IsCompleted согласно источнику:

private const int TASK_STATE_COMPLETED_MASK = TASK_STATE_CANCELED | TASK_STATE_FAULTED 
                                              | TASK_STATE_RAN_TO_COMPLETION;

Согласно ответу @ Mar c, ожидающему уже IsCompleted Задание замыкает короткое замыкание ожидающего, и выполнение будет продолжаться синхронно в том же потоке.

Согласно моему комментарию, было бы весьма необычно для непосредственного await Задача, созданная Task.CompletedTask или Task.FromResult, так как этот компилятор генерирует ненужную asyn c оболочку конечного автомата , что является полным перерасходом в сценарии ОП.

Распространенным сценарием использования различных завершенных методов фабрики заданий может быть макет во время модульного тестирования, когда для смоделированного класса / интерфейса требуется вернуть Task, но в остальном нет необходимости в * 1 023 *. Например, если следующий производственный интерфейс нуждался в насмешке или окурке:

public interface ISomeInterface
{
     Task<DateTime> GetDateAsync();
}

, который может быть задан следующим образом:

public class MyMock : ISomeInterface
{
    public Task<DateTime> GetDateAsync() // no async
    {
        // Directly return a completed task return a fake result
        return Task.FromResult(new DateTime(2019, 11, 12, 0, 0, 0, DateTimeKind.Utc));
    }
}

Затем будет ожидать тестируемый производственный класс (SUT) результат GetDateAsync() для введенного (и теперь поддельного) ISomeInterface, и, как правило, тот, кто вызвал метод, просто штамповал Задачу и вернул поддельные данные синхронно.

...