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
から変化することを期待していました。
どのようにしてこの状態を維持し、アクティブにすることができますか?
私の答えとしては、TPL( Task-Parallel-Library )、Task
クラス、およびTaskStatus
列挙がasync-awaitキーワードとasync-awaitキーワードは、TPLの当初の動機ではありませんでした。
async
とマークされたメソッドのコンテキストでは、結果のTask
は、メソッドの実行を表すTask
ではなく、メソッドの継続を表すTask
です。
これは、いくつかの可能な状態のみを使用できます。
Running
はWaitingForActivation
よりも優れたデフォルトであるように見えるかもしれませんが、ほとんどの場合、非同期メソッドがexecutedは実際には実行されていません(つまり、await
--何か他のものを実行している可能性があります)。他のオプションはTaskStatus
に新しい値を追加することでしたが、これは既存のアプリケーションとライブラリの重大な変更でした。
これはすべて、元のTPLの一部である_Task.Run
_を使用する場合とは大きく異なり、TaskStatus
列挙のすべての可能な値を使用することができます。
非同期メソッドのステータスを追跡したい場合は、 IProgress(T)
インターフェイスを見てください。これにより、進行中の進捗を報告できます。このブログ投稿 4.5の非同期:非同期APIでの進行状況とキャンセルの有効化 は、IProgress(T)
インターフェイスの使用に関する詳細情報を提供します。
理由は、あなたの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
:
より良い説明のためにこれを考慮してください:あなたはFoo
メソッドを持っている、それを言ってみましょうタスクA、そしてあなたはその中にTask
を持っています、それを言ってみましょうタスクB、今では実行中タスクはタスクB、あなたのタスクAを待っています- タスクBの結果。そして、返されるTask
に結果変数を割り当てます。これはタスクA、ですタスクBはタスクを返さないため、string
を返します。このことを考慮:
次のように結果を定義する場合:
Task result = Foo(5);
エラーは発生しませんが、次のように定義すると:
string result = Foo(5);
あなたが取得します:
「System.Threading.Tasks.Task」タイプを「string」に暗黙的に変換することはできません
ただし、await
キーワードを追加する場合:
string result = await Foo(5);
繰り返しますが、エラーは発生しませんが、結果(文字列)を待って結果変数に代入するため、最後にFooメソッドに2つのタスクを追加する場合、これを考慮してください:
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がこれら2つのタスクを待っているためです。
このコードは私にとって問題に対処しているようです。それはストリーミングクラスのために来て、いくつかの命名法をエルゴします。
''' <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
同じ問題がありました。答えは私を正しい軌道に乗せました。したがって、問題は、非同期でマークされた関数が期待どおりに関数自体のタスクを返さないことです(ただし、関数の別の継続タスク)。
そのため、「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.