it-swarm.com.de

Wie kann ich diese beiden Methoden gleichzeitig in .NET 4.5 ausführen?

Ich habe eine Methode, die 2 nabhängige Teile der Logik macht. Ich hatte gehofft, dass ich beide ausführen kann zur gleichen Zeit .. und erst danach weitermachen kann, wenn beide untergeordneten Methoden abgeschlossen sind.

Ich habe versucht, mich mit der Syntax async/await Vertraut zu machen, aber ich verstehe sie einfach nicht.

Hier ist der Code:

public PewPew SomeMethod(Foo foo)
{
    var cats = GetAllTheCats(foo);
    var food = GetAllTheFood(foo);

    return new PewPew
               {
                   Cats = cats,
                   Food = food
               };
}

private IList<Cat> GetAllTheCats(Foo foo)
{
    // Do stuff, like hit the Db, spin around, dance, jump, etc...
    // It all takes some time.
    return cats;
}

private IList<Food> GetAllTheFood(Foo foo)
{
    // Do more stuff, like hit the Db, nom nom noms...
    // It all takes some time.
    return food;
}

Mit dem obigen Code möchte ich also sagen: Holen Sie sich alle Katzen und das Futter gleichzeitig. Wenn wir fertig sind, geben Sie ein neues PewPew zurück.

Ich bin verwirrt, weil ich nicht sicher bin, welche Klassen oben async sind oder Task zurückgeben usw. Alle von ihnen? nur die zwei privaten? Ich schätze auch, dass ich die Task.WaitAll(tasks) -Methode nutzen muss, bin mir aber nicht sicher, wie ich setup die Aufgaben gleichzeitig ausführen soll.

Vorschläge, nette Leute?

39
Pure.Krome

Folgendes möchten Sie vielleicht tun:

public async Task<PewPew> SomeMethod(Foo foo)
{
    // get the stuff on another thread 
    var cTask = Task.Run(() => GetAllTheCats(foo));
    var fTask = Task.Run(() => GetAllTheFood(foo));

    var cats = await cTask;
    var food = await fTask;

    return new PewPew
               {
                   Cats = cats,
                   Food = food
               };
}

public IList<Cat> GetAllTheCats(Foo foo)
{
    // Do stuff, like hit the Db, spin around, dance, jump, etc...
    // It all takes some time.
    return cats;
}

public IList<Food> GetAllTheFood(Foo foo)
{
    // Do more stuff, like hit the Db, nom nom noms...
    // It all takes some time.
    return food;
}

Es gibt zwei Dinge, die Sie hier verstehen müssen:

1) Was ist der Unterschied zwischen diesen:

var cats = await cTask;
var food = await fTask;

Und das:

Task.WaitAll(new [] {cTask, fTask});

Beides ergibt ein ähnliches Ergebnis in dem Sinne, dass die 2 async Tasks beendet werden und dann return new PewPew - der Unterschied ist jedoch, dass Task.WaitAll() den aktuellen Thread blockiert (falls dies der Fall ist) Benutzeroberflächenthread, dann friert die Benutzeroberfläche ein. Stattdessen wird await das SomeMethod -Sprichwort in einer Zustandsmaschine aufschlüsseln und vom SomeMethod zum Aufrufer zurückkehren, wenn er auf das await -Schlüsselwort stößt. Der Thread wird nicht blockiert. Der Code unter await soll ausgeführt werden, wenn die Task async beendet ist.

2) Sie können dies auch tun:

var cats = await Task.Run(() => GetAllTheCats(foo));
var food = await Task.Run(() => GetAllTheFood(foo));

Dies startet jedoch nicht gleichzeitig die Tasks async. Die zweite Aufgabe beginnt, nachdem die erste beendet ist. Das liegt daran, wie das Schlüsselwort await funktioniert, hoffe, das hilft ...

BEARBEITEN: Wie benutzt man SomeMethod - irgendwo am Anfang des Aufrufbaums muss man die Eigenschaft Wait() oder Result benutzen - OR = - Sie müssen await von async void. Im Allgemeinen wäre async void ein Ereignishandler:

public async void OnSomeEvent(object sender, EventArgs ez) 
{ 
  Foo f = GetFoo();
  PewPew p = await SomeMethod(f);
}

Wenn nicht, verwenden Sie die Eigenschaft Result.

public Foo2 NonAsyncNonVoidMethod() 
{
   Foo f = GetFoo();
   PewPew p = SomeMethod(f).Result; //But be aware that Result will block thread

   return GetFoo2(p);
}
51
YK1

Mit Abstand am einfachsten geht das mit Parallel.Invoke()

IList<Cat> cats;
IList<Food> food;

Parallel.Invoke
(
    () => cats = GetAllTheCats(foo),
    () => food = GetAllTheFood(foo)
);

Parallel.Invoke() wartet auf die Rückkehr aller Methoden, bevor sie selbst zurückkehren.

Weitere Informationen finden Sie hier: http://msdn.Microsoft.com/en-us/library/dd460705.aspx

Beachten Sie, dass Parallel.Invoke() die Skalierung auf die Anzahl der Prozessoren in Ihrem System übernimmt, aber das ist nur dann wirklich wichtig, wenn Sie mehr als nur ein paar Aufgaben starten.

20
Matthew Watson

Sie müssen Async nicht verwenden, wenn Sie keine Async-Methode verwenden oder eine ältere Version des .Net-Frameworks verwenden. Verwenden Sie einfach Tasks zur Vereinfachung:

Task taskA = Task.Factory.StartNew(() => GetAllTheCats(foo));
Task taskB = Task.Factory.StartNew(() => GetAllTheFood(foo));

Task.WaitAll(new [] { taskA, taskB });
// Will continue after both tasks completed
10
Adam Tal

Sie können das TPL verwenden, um auf mehrere Aufgaben zu warten, während diese ausgeführt werden. Siehe hier .

So was:

public PewPew SomeMethod(Foo foo) {
    IList<Cat> cats = null;
    IList<Food> foods = null;

    Task[] tasks = new tasks[2] {
        Task.Factory.StartNew(() => { cats = GetAllTheCats(foo); }),
        Task.Factory.StartNew(() => { food = GetAllTheFood(foo); })
    };

    Task.WaitAll(tasks);

    return new PewPew
               {
                   Cats = cats,
                   Food = food
               };
}
0
Maarten

Wenn Sie die anderen Antworten ergänzen, können Sie Folgendes tun:

public PewPew SomeMethod(Foo foo)
{
    Task<IList<Cat>> catsTask = GetAllTheCatsAsync(foo);
    Task<IList<Food>> foodTask = GetAllTheFoodAsync(foo);

    // wait for both tasks to complete
    Task.WaitAll(catsTask, foodTask);

    return new PewPew
    {
        Cats = catsTask.Result,
        Food = foodTask.Result
    };
}

public async Task<IList<Cat>> GetAllTheCatsAsync(Foo foo)
{
    await Task.Delay(7000); // wait for a while
    return new List<Cat>();
}

public async Task<IList<Food>> GetAllTheFoodAsync(Foo foo)
{
    await Task.Delay(5000); // wait for a while
    return new List<Food>();
}
0