web-dev-qa-db-ja.com

非同期は常にWaitingForActivation

asyncawaitキーワードが何であるかを理解しようとしていますが、出力は期待したものではありません。

コンソールアプリケーションは次のとおりです。

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から変化することを期待していました。

どのようにしてこの状態を維持し、アクティブにすることができますか?

48
BanksySan

私の答えとしては、TPL( Task-Parallel-Library )、Taskクラス、およびTaskStatus列挙がasync-awaitキーワードとasync-awaitキーワードは、TPLの当初の動機ではありませんでした。

asyncとマークされたメソッドのコンテキストでは、結果のTaskは、メソッドの実行を表すTaskではなく、メソッドの継続を表すTaskです。

これは、いくつかの可能な状態のみを使用できます。

  • キャンセル
  • 故障
  • RanToCompletion
  • WaitingForActivation

RunningWaitingForActivationよりも優れたデフォルトであるように見えるかもしれませんが、ほとんどの場合、非同期メソッドがexecutedは実際には実行されていません(つまり、await--何か他のものを実行している可能性があります)。他のオプションはTaskStatusに新しい値を追加することでしたが、これは既存のアプリケーションとライブラリの重大な変更でした。

これはすべて、元のTPLの一部である_Task.Run_を使用する場合とは大きく異なり、TaskStatus列挙のすべての可能な値を使用することができます。

非同期メソッドのステータスを追跡したい場合は、 IProgress(T) インターフェイスを見てください。これにより、進行中の進捗を報告できます。このブログ投稿 4.5の非同期:非同期APIでの進行状況とキャンセルの有効化 は、IProgress(T)インターフェイスの使用に関する詳細情報を提供します。

56
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メソッドを持っている、それを言ってみましょうタスク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つのタスクを待っているためです。

20
Selman Genç

このコードは私にとって問題に対処しているようです。それはストリーミングクラスのために来て、いくつかの命名法をエルゴします。

''' <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
1
David

同じ問題がありました。答えは私を正しい軌道に乗せました。したがって、問題は、非同期でマークされた関数が期待どおりに関数自体のタスクを返さないことです(ただし、関数の別の継続タスク)。

そのため、「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