Подтвердить что ты не робот

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 после запуска метода.

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

4b9b3361

Ответ 1

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

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

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

  • Отменено
  • Faulted
  • RanToCompletion
  • WaitingForActivation

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

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

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

Ответ 2

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

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, скажем, Задача A, и в нем есть Task, скажем, Задача B. Теперь задача работает, Задача B, ваша Задача A ожидает Задача B результат. И вы оцениваете свою переменную result возвращаемой Task, которая Задача A,, потому что Задача B не возвращает задачу, она возвращает 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 ждет этих двух задач.

Ответ 3

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

Так что это ключевые слова "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.

Ответ 4

этот код, кажется, решает проблему для меня. это идет для потокового класса, эго часть номенклатуры.

''' <summary> Reference to the awaiting task. </summary>
''' <value> The awaiting task. </value>
Protected ReadOnly Property AwaitingTask As Threading.Tasks.Task

''' <summary> Reference to the Action task; this task status undergoes changes. </summary>
Protected ReadOnly Property ActionTask As Threading.Tasks.Task

''' <summary> Reference to the cancellation source. </summary>
Protected ReadOnly Property TaskCancellationSource As Threading.CancellationTokenSource

''' <summary> Starts the action task. </summary>
''' <param name="taskAction"> The action to stream the entities, which calls
'''                           <see cref="StreamEvents(Of T)(IEnumerable(Of T), IEnumerable(Of Date), Integer, String)"/>. </param>
''' <returns> The awaiting task. </returns>
Private Async Function AsyncAwaitTask(ByVal taskAction As Action) As Task
    Me._ActionTask = Task.Run(taskAction)
    Await Me.ActionTask '  Task.Run(streamEntitiesAction)
    Try
        Me.ActionTask?.Wait()
        Me.OnStreamTaskEnded(If(Me.ActionTask Is Nothing, TaskStatus.RanToCompletion, Me.ActionTask.Status))
    Catch ex As AggregateException
        Me.OnExceptionOccurred(ex)
    Finally
        Me.TaskCancellationSource.Dispose()
    End Try
End Function

''' <summary> Starts Streaming the events. </summary>
''' <exception cref="InvalidOperationException"> Thrown when the requested operation is invalid. </exception>
''' <param name="bucketKey">            The bucket key. </param>
''' <param name="timeout">              The timeout. </param>
''' <param name="streamEntitiesAction"> The action to stream the entities, which calls
'''                                     <see cref="StreamEvents(Of T)(IEnumerable(Of T), IEnumerable(Of Date), Integer, String)"/>. </param>
Public Overridable Sub StartStreamEvents(ByVal bucketKey As String, ByVal timeout As TimeSpan, ByVal streamEntitiesAction As Action)
    If Me.IsTaskActive Then
        Throw New InvalidOperationException($"Stream task is {Me.ActionTask.Status}")
    Else
        Me._TaskCancellationSource = New Threading.CancellationTokenSource
        Me.TaskCancellationSource.Token.Register(AddressOf Me.StreamTaskCanceled)
        Me.TaskCancellationSource.CancelAfter(timeout)
        ' the action class is created withing the Async/Await function
        Me._AwaitingTask = Me.AsyncAwaitTask(streamEntitiesAction)
    End If
End Sub

Ответ 5

Я преодолел эту проблему, если кто-то заинтересован. В методе myMain я назвал мой метод readasync как

Dispatcher.BeginInvoke(new ThreadStart(() => ReadData()));

Теперь у меня все хорошо.