webentwicklung-frage-antwort-db.com.de

C # Threading/Async: Ausführen einer Aufgabe im Hintergrund, während die Benutzeroberfläche interaktiv ist

Nachdem ich mich in Async/Await und Threading umgesehen habe, bin ich immer noch nicht sicher, wie ich es auf meine Situation anwenden kann. Unabhängig von der Variante, die ich ausprobiere, hängt meine Benutzeroberfläche immer noch, weil ich meine gewünschte Funktion nicht asynchron aufzurufen scheint. Außerdem kann es sein, dass ich für meine Lösung Threading benötige. 

Was ich versuche zu tun: Ich habe eine WPF-Anwendung, auf der sich eine Schaltfläche befindet, mit der ich eine Operation starten möchte, die noch Interaktion mit dem Programm, über die Benutzeroberfläche oder auf andere Weise ermöglicht. Sobald eine Bedingung erfüllt ist, die außerhalb dieser Funktion festgelegt wurde, sollte die Funktion beendet werden. Für mich klingt das ziemlich normal, aber ich habe das Gefühl, dass ich etwas missverstehe und ich es falsch implementiert habe.

Was ich gerade habe:  

private async void start_button_Click(object sender, RoutedEventArgs e)
{
    await StaticClass.MyFunction();
}

private void stop_button_Click(object sender, RoutedEventArgs e)
{
    StaticClass.stopFlag = true;
}

public static Task<int> myFunction()
{
    //Stuff Happens

    while(StaticClass.stopFlag == false)
        //Do Stuff

    //Stuff Happens

    return Task.FromResult(1) //I know this is bad, part of the reason I'm asking
}

Ich habe gehofft, ein paar Anleitungen zu erhalten, wenn ich mich dem richtigen Weg nähere und Einsicht in was ich falsch mache.

15
Karoly S

Sie haben es definitiv falsch implementiert. Sie geben einen Task<int> zurück, aber nur nachdem alle Arbeiten bereits erledigt wurden .

Es scheint mir, dass Sie wahrscheinlich nur eine synchrone Methode haben sollten:

private static void MyFunction()
{
    // Loop in here
}

Dann starte eine Aufgabe dafür wie folgt:

Task task = Task.Run((Action) MyFunction);

Sie können diese Aufgabe dann abwarten, wenn Sie möchten - obwohl es in dem von Ihnen genannten Beispiel keinen Sinn macht, dies zu tun, da Sie sowieso nichts nach await tun.

Ich stimme auch mit Reed überein, dass die Verwendung einer CancellationToken sauberer wäre als ein statisches Flag an anderer Stelle.

24
Jon Skeet

Du hast falsch verstanden.

public static Task<int> myFunction()
{
    //Stuff Happens

    while(StaticClass.stopFlag == false)
        //Do Stuff

    //Stuff Happens

    return Task.FromResult(1) //I know this is bad, part of the reason I'm asking
}

Der gesamte Code passiert immer noch beim ersten Aufruf von await StaticClass.MyFunction();, er gibt niemals die Kontrolle an den Anrufer zurück. Sie müssen den Schleifenteil in einen separaten Thread einfügen.

public static async Task myFunction()
{
    //Stuff Happens on the original UI thread

    await Task.Run(() => //This code runs on a new thread, control is returned to the caller on the UI thread.
    {
        while(StaticClass.stopFlag == false)
            //Do Stuff
    });

    //Stuff Happens on the original UI thread after the loop exits.
}
7

Anstatt zu versuchen, eine bool dafür zu verwenden, sollten Sie das in das Framework eingebaute managed Cancellation Framework verwenden.

Im Grunde würden Sie eine CancellationTokenSource erstellen und eine CancellationToken an Ihre Methode übergeben, die zur Abwicklung verwendet werden könnte.

Schließlich wird Ihre aktuelle Methode niemals aus dem UI-Thread entfernt. Sie müssen Task.Run oder Ähnliches verwenden, um die Methode in den ThreadPool zu verschieben, wenn Sie die Benutzeroberfläche nicht blockieren möchten.

3
Reed Copsey

Als Alternative können Sie mit BackgroundWorker class arbeiten, um die Arbeit zu erledigen. Ihre Benutzeroberfläche bleibt interaktiv.

1
Eugene