Почему мой TCS не ждет?

async Ключевое слово действительно вызывает изменение CIL (даже если в методе нет ожидания), но в первую очередь это позволяет await присутствовать.

Но я не ожидал, что произойдет следующее:

static void Main(string[] args)
{
    Task t = Go();
    t.Wait();
}

static async Task Go()
{
    Console.WriteLine(1);
    await AAA(3000);
    Console.WriteLine(2);
}


static  Task<object> AAA(int a) // <--- No `async`
{
    TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
    Task.Delay(a).ContinueWith(b => tcs.SetResult(null));
    return tcs.Task;
}

Этот принт:

1
(wait)
2

Но если я изменю

static  Task<object> AAA(int a) 

в

static async  Task<object> AAA(int a) 

Это печатает:

1
2
(no wait)

Вопрос

Почему я не вижу задержки? TCS разрешается только через три секунды. Между тем, задача не решена и ее следует ждать.

3 ответа

Решение

Без async Ключевое слово, которое вы возвращаете TaskCompletionSourceзадача от AAA и поэтому вы ждете его завершения (что произойдет после завершения задержки).

Тем не менее, когда вы добавляете async ключевое слово, задача, возвращаемая методом, является задачей конечного автомата, которая выполняется синхронно. Эта задача имеет внутри (в результате) TaskCompletionSourceэто задача, но это не та задача, которую вы ждете.

Если вы хотите, чтобы этот метод ожидал TaskCompletionSourceзадача, которую вы можете ждать внутреннюю задачу Task<Task>:

await ((Task) await AAA(3000));

Или жду TaskCompletionSourceвместо того, чтобы вернуть его:

async Task AAA(int a)
{
    TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
    Task.Delay(a).ContinueWith(b => tcs.SetResult(null));
    await tcs.Task;
}

Или даже лучше, просто в ожидании Task.Delay сам:

async Task<object> AAA(int a)
{
    await Task.Delay(a);
    return null;
}

Потому что, когда вы вернетесь Task из асинхронного метода с типом возврата Task<object> что вы получите Task<Task> с вашей задачей (которую вы ожидаете ожидать) внутри. Вот что вы должны сделать:

static async Task<object> AAA(int a)
{
  await Task.Delay(a);
  return null;
}

Короче говоря, старайтесь избегать смешивания асинхронных и прямых задач в одном методе.

Как упоминал Сергей, посмотрите на этот пример в Visual Studio:

        static async Task<int> AAA(int a) // <--- No `async`
        {
             TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
             Task.Delay(a).ContinueWith(b =>
             {
                  tcs.SetResult(1);
             });
             return tcs.Task;
        }

Вы получите ошибку, как это:

Поскольку это асинхронный метод, возвращаемое выражение должно иметь тип int, а не Task, поскольку ваш метод будет выполняться синхронно. Вот почему необходимо вернуть intне Task,

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