it-swarm.com.de

Async immer auf sich wartenFürAktivierung

Ich versuche herauszufinden, worum es bei den async & await-Schlüsselwörtern geht, jedoch ist die Ausgabe nicht das, was ich erwarte.

Die Konsolenanwendung sieht wie folgt aus:

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.";
            });
    }
}

Die Ausgabe ist:

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..

Ich hatte erwartet, dass sich der Status von WaitingForActivation ändert, sobald die Methode gestartet wird.

Wie kann es in diesem Zustand bleiben und aktiv sein?

37
BanksySan

Für meine Antwort muss daran erinnert werden, dass die TPLTask-Parallel-Library, Task-Klasse und TaskStatus-Enumeration vor den Schlüsselwörtern async-await eingeführt wurden und die Schlüsselwörter async-await nicht die ursprüngliche Motivation der TPL.

Im Zusammenhang mit Methoden, die als async gekennzeichnet sind, ist die resultierende Task keine Task, die die Ausführung der Methode darstellt, sondern eine Task für die Fortsetzung der Methode. 

Dies kann nur einige wenige mögliche Zustände nutzen:

  • Abgebrochen
  • Fehlerhaft
  • RanToCompletion
  • Auf Aktivierung warten

Ich verstehe, dass Running scheinbar ein besserer Standard war als WaitingForActivation, dies könnte jedoch irreführend sein, da die asynchrone Methode meistens _ ist (ausgeführt =/ist nicht wirklich aktiv (dh await- etwas anderes) Die andere Option bestand möglicherweise darin, einen neuen Wert zu TaskStatus hinzuzufügen, dies könnte jedoch eine grundlegende Änderung für vorhandene Anwendungen und Bibliotheken gewesen sein. 

Dies alles unterscheidet sich sehr von der Verwendung von Task.Run, das Teil der ursprünglichen TPL ist, und kann alle möglichen Werte der TaskStatus-Enumeration verwenden.

Wenn Sie den Status einer asynchronen Methode verfolgen möchten, sehen Sie sich die Schnittstelle IProgress(T) an. Auf diese Weise können Sie den laufenden Fortschritt melden. In diesem Blogbeitrag Async in 4.5: Fortschritt und Abbruch in Async-APIs aktivieren werden weitere Informationen zur Verwendung der IProgress(T)-Schnittstelle bereitgestellt.

46
Lukazoid

Der Grund dafür ist, dass Ihr result dem zurückgegebenen Task zugewiesen ist, der die Fortsetzung Ihrer Methode darstellt, und dass Ihre laufende Methode eine andere Aufgabe enthält. Wenn Sie diese Aufgabe direkt zuweisen, erhalten Sie die erwarteten Ergebnisse :

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);

Der output:

enter image description here

Betrachten Sie dies zur besseren Erklärung: Sie haben eine Methode Foo, sagen wir es Aufgabe A, und Sie haben eine Methode Task, sagen wir es Aufgabe B, Nun ist die laufende Aufgabe Aufgabe B, Ihre Aufgabe A und wartet auf Ergebnis Aufgabe B Sie ordnen Ihre Ergebnisvariable Ihrem zurückgegebenen Task zu. Dies ist Task A,, da Task B keine Task zurückgibt, es wird ein string zurückgegeben. Bedenken Sie:

Wenn Sie Ihr Ergebnis so definieren:

Task result = Foo(5);

Sie werden keinen Fehler bekommen. Aber wenn Sie es so definieren:

string result = Foo(5);

Sie erhalten:

Typ 'System.Threading.Tasks.Task' kann nicht implizit in 'string' konvertiert werden

Wenn Sie jedoch ein await-Schlüsselwort hinzufügen:

string result = await Foo(5);

Auch hier werden Sie keinen Fehler erhalten. Weil es auf das Ergebnis (String) wartet und es Ihrer Ergebnisvariablen zuweist. Berücksichtigen Sie dies als letztes, wenn Sie Ihrer Foo-Methode zwei Aufgaben hinzufügen:

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.";
        });
}

Und wenn Sie die Anwendung ausführen, erhalten Sie die gleichen Ergebnisse. (WaitingForActivation) Da jetzt Ihre Aufgabe A auf diese beiden Aufgaben wartet.

20
Selman Genç

dieser Code scheint das Problem für mich behoben zu haben. Es kommt für eine Streaming-Klasse, also ein Teil der Nomenklatur.

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

Ich hatte das gleiche Problem. Die Antworten haben mich auf den richtigen Weg gebracht. Das Problem ist also, dass mit async markierte Funktionen nicht wie erwartet eine Task der Funktion selbst zurückgeben (sondern eine weitere Folgeaufgabe der Funktion).

Es sind also die Schlüsselwörter "Erwarten" und "Asynchron", die das Ganze vermasseln. Die einfachste Lösung besteht darin, sie einfach zu entfernen. Dann funktioniert es wie erwartet. Wie in:

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.";
    });
}

Welche Ausgänge:

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