it-swarm.com.ru

Async всегда WaitingForActivation

Я пытаюсь выяснить, что такое ключевые слова async & await, но результат не тот, который я ожидаю.

Консольное приложение выглядит следующим образом:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Foo called");
        var result = Foo(5);

        while (result.Status != TaskStatus.RanToCompletion)
        {
            Console.WriteLine("Thread ID: {0}, Status: {1}", Thread.CurrentThread.ManagedThreadId, result.Status);
            Task.Delay(100).Wait();
        }

        Console.WriteLine("Result: {0}", result.Result);
        Console.WriteLine("Finished.");
        Console.ReadKey(true);
    }

    private static async Task<string> Foo(int seconds)
    {
        return await Task.Run(() =>
            {
                for (int i = 0; i < seconds; i++)
                {
                    Console.WriteLine("Thread ID: {0}, second {1}.", Thread.CurrentThread.ManagedThreadId, i);
                    Task.Delay(TimeSpan.FromSeconds(1)).Wait();
                }

                return "Foo Completed.";
            });
    }
}

Результат:

Foo called
Thread ID: 10, Status: WaitingForActivation
Thread ID: 6, second 0.
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 6, second 1.
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 6, second 2.
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 6, second 3.
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 6, second 4.
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Result: Foo Completed.
Finished..

Я ожидал увидеть изменение статуса с WaitingForActivation после запуска метода.

Как он может оставаться в этом состоянии и быть активным?

37
BanksySan

Что касается моего ответа, то стоит помнить, что перечисления TPL ( Task-Parallel-Library ), Task и TaskStatus были введены до появления ключевых слов async-await, а ключевые слова async-await не были исходной мотивацией TPL.

В контексте методов, помеченных как async, результирующая Task - это не Task, представляющая выполнение метода, а Task для продолжения метода. 

Это позволяет использовать только несколько возможных состояний:

  • Отменен
  • Разорванный
  • RanToCompletion
  • WaitingForActivation

Я понимаю, что Running может показаться лучше по умолчанию, чем WaitingForActivation, однако это может вводить в заблуждение, поскольку в большинстве случаев асинхронный метод, выполняемый execute, на самом деле не выполняется (то есть может быть await- что-то еще ). Другой вариант, возможно, заключался в добавлении нового значения в TaskStatus, однако это могло быть серьезным изменением для существующих приложений и библиотек. 

Все это сильно отличается от того, что при использовании Task.Run, который является частью исходного TPL, он может использовать все возможные значения перечисления TaskStatus.

Если вы хотите отслеживать состояние асинхронного метода, взгляните на интерфейс IProgress(T) , это позволит вам сообщить о текущем прогрессе. Этот пост в блоге, Async в 4.5: Включение выполнения и отмены в API-интерфейсах Async предоставит дополнительную информацию об использовании интерфейса IProgress(T).

46
Lukazoid

Причина в том, что ваша result назначена возвращающему Task, который представляет собой продолжение вашего method, и у вас есть другая Задача в вашем методе, которая выполняется, если вы непосредственно назначите Задачу, подобную этой, вы получите ожидаемые результаты:

var task = Task.Run(() =>
        {
            for (int i = 10; i < 432543543; i++)
            {
                // just for a long job
                double d3 = Math.Sqrt((Math.Pow(i, 5) - Math.Pow(i, 2)) / Math.Sin(i * 8));
            }
           return "Foo Completed.";

        });

        while (task.Status != TaskStatus.RanToCompletion)
        {
            Console.WriteLine("Thread ID: {0}, Status: {1}", Thread.CurrentThread.ManagedThreadId,task.Status);

        }

        Console.WriteLine("Result: {0}", task.Result);
        Console.WriteLine("Finished.");
        Console.ReadKey(true);

output:

enter image description here

Рассмотрим это для лучшего объяснения: У вас есть метод Foo, скажем, Task A, и у вас есть Task, скажем, Task B, теперь running task, это Task B, ваш Task A ожидает Task B. И вы присваиваете свою переменную результата возвращающему Task, который является Task A,, потому что Задача B не возвращает Task, она возвращает string. Учти это:

Если вы определите свой результат следующим образом:

Task result = Foo(5);

Вы не получите никакой ошибки. Но если вы определите это так:

string result = Foo(5);

Ты получишь:

Невозможно неявно преобразовать тип 'System.Threading.Tasks.Task' в 'string' 

Но если вы добавите ключевое слово await:

string result = await Foo(5);

Опять же, вы не получите никакой ошибки. Потому что он будет ждать результат (строку) и присваивать его вашей переменной результата. Так что, если вы добавите две задачи в свой метод Foo, обратите внимание на последнее:

private static async Task<string> Foo(int seconds)
{
    await Task.Run(() =>
        {
            for (int i = 0; i < seconds; i++)
            {
                Console.WriteLine("Thread ID: {0}, second {1}.", Thread.CurrentThread.ManagedThreadId, i);
                Task.Delay(TimeSpan.FromSeconds(1)).Wait();
            }

            // in here don't return anything
        });

   return await Task.Run(() =>
        {
            for (int i = 0; i < seconds; i++)
            {
                Console.WriteLine("Thread ID: {0}, second {1}.", Thread.CurrentThread.ManagedThreadId, i);
                Task.Delay(TimeSpan.FromSeconds(1)).Wait();
            }

            return "Foo Completed.";
        });
}

И если вы запустите приложение, вы получите те же результаты. (WaitingForActivation) Потому что теперь ваша задача A ожидает эти две задачи.

18
Selman Genç

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

Так что это ключевые слова "await" и "async", которые портят дело. Самое простое решение - просто удалить их. Тогда все работает как положено. Как в:

static void Main(string[] args)
{
    Console.WriteLine("Foo called");
    var result = Foo(5);

    while (result.Status != TaskStatus.RanToCompletion)
    {
        Console.WriteLine("Thread ID: {0}, Status: {1}", Thread.CurrentThread.ManagedThreadId, result.Status);
        Task.Delay(100).Wait();
    }

    Console.WriteLine("Result: {0}", result.Result);
    Console.WriteLine("Finished.");
    Console.ReadKey(true);
}

private static Task<string> Foo(int seconds)
{
    return Task.Run(() =>
    {
        for (int i = 0; i < seconds; i++)
        {
            Console.WriteLine("Thread ID: {0}, second {1}.", Thread.CurrentThread.ManagedThreadId, i);
            Task.Delay(TimeSpan.FromSeconds(1)).Wait();
        }

        return "Foo Completed.";
    });
}

Какие выводы:

Foo called
Thread ID: 1, Status: WaitingToRun
Thread ID: 3, second 0.
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 3, second 1.
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 3, second 2.
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 3, second 3.
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 3, second 4.
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Result: Foo Completed.
Finished.
0
someone else