Task.Delay() не ведет себя так, как ожидалось

Task.Delay() не ведет себя так, как ожидалось, или, скорее, я не понимаю, что он должен делать. Я пытаюсь окунуться в Task в С# и как заменить Thread в моей реализации.

То, что я пытаюсь сделать, это примерно так:

  • Пока верно
    • напечатать строку
    • подождите секунду,
    • Если условие выполнено, цикл выхода, в противном случае сохранить цикл

У меня это реализовано с помощью Threads отлично, но все классные дети говорят, что я должен использовать Task и не трогать Thread.

Итак, для кода у меня есть это (Игнорировать [Test] - это просто удобный способ опробовать вещи)

    [Test]
    public void ChattyTask()
    {
        var chattyTask = new Task(ChattyWriter);
        chattyTask.Start();
        chattyTask.Wait();
    }

    public void ChattyWriter()
    {
        int count = 0;
        while (true)
        {
            var message = String.Format("Chatty Writer number {0}", count);
            Trace.WriteLine(message);
            count++;
            Task.Delay(1000);

            if (count >= 20)
            {
                break;
            }
        }
    }

Когда я запускаю это, тест заканчивается в миллисекундах, а не через 20 секунд, как я ожидал. Если я заменяю Task.Delay() на Thread.Sleep(), все работает так, как ожидалось, и я получаю распечатку один раз в секунду. Я попытался добавить async и await в ChattyWriter(), но не только не добавил 1 секунду задержки, он печатал только одну строку вместо 20.

Что я делаю неправильно?

Вероятно, это поможет описать, что я делаю: мой проект работает с внешним API (RESTful), и после запроса некоторых задач мне нужно опросить API, чтобы проверить, завершена ли задача. Внешние задачи могут быть длительными: 1-15 минут. Так что мне нужна задержка между проверкой на завершение. И может быть много разных параллельных процессов, выполняемых с несколькими внешними задачами. Я понимаю, что если я использую Thread.Sleep() при опросе, другие процессы на одном и том же Thread будут заблокированы без уважительной причины.

Ответ 1

Task.Delay возвращает объект Task, на который вы должны ждать. В противном случае следующий код будет выполнен немедленно. Поэтому вы должны объявить свой метод как async. Тогда вы можете подождать Task.Delay

public async Task ChattyWriter()
{
    int count = 0;
    while (true)
    {
        var message = String.Format("Chatty Writer number {0}", count);
        Trace.WriteLine(message);
        count++;
        await Task.Delay(1000);
           ...
    }
}

Вы должны каким-то образом прервать свой вызывающий поток. unit test завершается и фоновый поток. Но если вы вызовете этот метод из пользовательского интерфейса, пользовательский интерфейс не будет заблокирован.

Вызывая Wait по асинхронному методу, вы получите тупик. Подробнее см. здесь.

Ответ 2

Task.Delay(1000); создает задачу async, которая будет завершена через секунду, но выполнение текущего метода будет продолжаться параллельно.

Чтобы дождаться завершения задачи, вы можете заменить

Task.Delay(1000);

с

Task.Delay(1000).Wait();

Ответ 3

Task.Delay() фактически создает задачу, поэтому вам просто нужно подождать.

Попробуйте это

        public static void ChattyWriter()
    {
        int count = 0;
        while (true)
        {
            var message = String.Format("Chatty Writer number {0}", count);
            Console.WriteLine(message);
            count++;
            var t = Task.Delay(1000);
            t.Wait();

            if (count >= 20)
            {
                break;
            }
        }
    }