Laden...

Wait Blockiert nicht (Async/Await) [gelöst]

Erstellt von DerHulk vor 10 Jahren Letzter Beitrag vor 10 Jahren 2.851 Views
DerHulk Themenstarter:in
270 Beiträge seit 2005
vor 10 Jahren
Wait Blockiert nicht (Async/Await) [gelöst]

Hallo Community hätte mal eine kleine Frage bezüglich Async und Await/Wait
könnte mir vielleicht einer gerade mal auf die Sprünge helfen 😃

Folgender Code:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Sandbox.Console
{
    public class Main
    {

        public void SomeMethod()
        {
            var runTask = new Task( async () => { var t = await SomelongRunningMethod();
                                                                    System.Console.WriteLine("Lambda has finished.");
                                                                    });
            runTask.Start();
            runTask.Wait();
            System.Console.WriteLine("Some method finished.");
        }

        private async Task<bool> SomelongRunningMethod()
        {
            var innerTask = new Task(() =>
            {
                System.Threading.Thread.Sleep(10000);
                System.Console.WriteLine("Sleep finished.");
            });
            innerTask.Start();
            await innerTask;

            System.Console.WriteLine("Some long running method finished.");
            return true;
        }

    }
}


Die Ausgabe ist dann

  1. Some method finished
  2. Sleep finished
  3. Some long running method finished
  4. lamda finished

Meine Frage ist nun einfach warum das Wait() nicht anspringt (wie ja What's the difference between Task.Start/Wait and Async/Await? ganz nett erklärt), sollte doch den Main-Thread solange blocken bis der Task durch ist. Und wie könnte ich das bewerkstelligen das Wait nun wartet (ohne die SomeMethode mit Zombie-Pattern-Async zu versehen) ?

Mir ist bewusst das man gerade den Main/UI-Thread frei bekommen möchte aber z.B. in einem Test ist dies ja gar nicht gewünscht. Danke schon mal!

Mit freundlichen Grüßen

DerHulk

49.485 Beiträge seit 2005
vor 10 Jahren

Hallo DerHulk,

warum benutzt du hier überhaupt async/async, wenn du im Main-Thread blockierend warten willst? Kommt mir unsinnig vor.

herbivore

6.911 Beiträge seit 2009
vor 10 Jahren

Hallo DerHulk,

das Beispiel aus dem verlinkten SO-Thread ist auch ganz anders aufgebaut als deines, das so in der Verwendung von Asynchronizität wirklich keinen Sinn macht, denn direkt nach dem Start eines async. Vorgangs per Wait-Methode auf diesen zu warten bringt nichts - da wäre ein synchroner Aufruf sinnvoller.

Schau dir mal Async/Await FAQ an - vllt. klärt sich dann dein Verständnis-Problem davon.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

DerHulk Themenstarter:in
270 Beiträge seit 2005
vor 10 Jahren

Danke schon mal für die Antworte, der Grund für den ganzen Spaß ist das ich einen BDD-Test habe der Synchron abläuft aber eine Methode aufrufen muss aus dem Produktiv-Code der Async ist.

6.911 Beiträge seit 2009
vor 10 Jahren

Hallo DerHulk,

welches Test-Framework verwendest du dazu? Einige können nämlich schon mit Task als Rückgabewert umgehen, so dass async/await ganz normal verwendet werden kann.

Falls das nicht möglich ist, so kapsle die async-Methode mit einer synchronen für das Testen:


internal static TResult RunAsyncTest<TArg, TResult>(Func<TArg, Task<TResult>> methodToTest, TArg argument)
{
    return methodToTest(argument).Result;    // Result verwendet intern Wait, falls der Task noch nicht fertig ist
}

Vewendung:


public Task<int> LongRunningMethod(string input)
{
    // Aufwändige CPU-Berechnung:
    return Task.Run(() => 
    {
        // irgendwas aufwändiges mit "input" machen
    });
}

public async Task<string> DatabaseQuery(int id)
{
    return await _repositoy.GetItemAsync(id).ConfigureAwait(false);
}


// Test 1:
int resultForTest1 = RunAsyncTest(foo.LongRunningMethod, "abcd");

// Test 2:
string resultForTest2 = RunAsyncTest(foo.DatabaseQuery(42));

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

1.346 Beiträge seit 2008
vor 10 Jahren

Das Problem liegt hier:

var runTask = new Task( async () => { var t = await SomelongRunningMethod();
                                                                     System.Console.WriteLine("Lambda has finished.");
                                                                     });

Du übergibst dem Task Konstruktor eine Action, also gibt der lambda Ausdruck ein void zurück. Also verhällt sich das ganze nach "Fire and Forget" und returned direkt an dem await un der Task ist damit beendet. Der Task weiss nicht das die Methode intern noch weiterläuft

DerHulk Themenstarter:in
270 Beiträge seit 2005
vor 10 Jahren

Ok vielen dank, denke ich hab es nun. Da ich den ersten Task nicht typisiert mit Rückgabewert aufbauen kann muss ich hier mit Result() arbeiten. Das Test-Framework ist übrigens Specflow.
Anbei der funktionsfähige Code


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Sandbox.Console
{
    public class Main
    {

        public void SomeMethod()
        {
             
            var runTask = new Task<bool>(  () => {      var t =  SomelongRunningMethode().Result;
                                                        System.Console.WriteLine("Lambda has finished.");
                                                        return true;
                                                                    });

            runTask.Start( );
            runTask.Wait( );
            System.Console.WriteLine("Some method finished.");
        }


        private async Task<bool> SomelongRunningMethod()
        {
            var innerTask = new Task(() =>
            {
                System.Threading.Thread.Sleep(10000);
                System.Console.WriteLine("Sleep finished.");
            });
            innerTask.Start();
            await innerTask;

            System.Console.WriteLine("Some long running method finished.");
            return true;
        }

    }
}