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
после запуска метода.
Как он может оставаться в этом состоянии и быть активным?
Ответы
Ответ 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()));
Теперь у меня все хорошо.