web-dev-qa-db-fra.com

Comment puis-je exécuter ces deux méthodes «en même temps» dans .NET 4.5?

J'ai une méthode qui fait 2 indépendant morceaux de logique. J'espérais pouvoir les exécuter tous les deux en même temps .. et continuer seulement après que ces deux méthodes enfants soient terminées.

J'essayais de comprendre la syntaxe async/await Mais je ne comprends tout simplement pas.

Voici le 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;
}

Donc, avec ce code ci-dessus, je veux dire: allez chercher tous les chats et la nourriture en même temps. Une fois que nous avons terminé, retournez un nouveau PewPew.

Je suis confus parce que je ne sais pas quelles classes ci-dessus sont async ou retournent un Task, etc. Tout ça? juste les deux privés? Je suppose également que je dois tirer parti de la méthode Task.WaitAll(tasks), mais je ne sais pas comment setup les tâches à exécuter en même temps.

Des suggestions, des gens aimables?

39
Pure.Krome

Voici ce que vous voudrez peut-être faire:

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

Il y a deux choses que vous devez comprendre ici:

1) Quelle est la différence entre ceci:

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

Et ça:

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

Les deux vous donneront un résultat similaire dans le sens où les 2 async tâches se termineront puis return new PewPew - cependant, la différence est que Task.WaitAll() bloquera le thread actuel (si c'est le cas) Thread d'interface utilisateur, puis l'interface utilisateur se fige). à la place, await décomposera le mot-clé SomeMethod dans une machine à états et reviendra du SomeMethod à son appelant lors de sa rencontre avec le mot clé await. Cela ne bloquera pas le fil. Le code ci-dessous await sera planifié pour s'exécuter lorsque la tâche async sera terminée.

2) Vous pouvez également faire ceci:

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

Cependant, cela ne démarrera pas simultanément les tâches async. La deuxième tâche commencera une fois la première terminée. En effet, comment fonctionne le mot clé await, j'espère que cela aide ...

EDIT: Comment utiliser SomeMethod - quelque part au début de l'arborescence des appels, vous devez utiliser la propriété Wait() ou Result - OR = - vous devez await à partir de async void. En général, async void serait un gestionnaire d'événements:

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

Sinon, utilisez la propriété 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

De loin, la façon la plus simple de le faire est d'utiliser Parallel.Invoke()

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

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

Parallel.Invoke() attendra le retour de toutes les méthodes avant de revenir.

Plus d'informations ici: http://msdn.Microsoft.com/en-us/library/dd460705.aspx

Notez que Parallel.Invoke() gère la mise à l'échelle vers le nombre de processeurs de votre système, mais cela n'a vraiment d'importance que si vous démarrez plus que quelques tâches.

20
Matthew Watson

Vous n'êtes pas obligé d'utiliser async si vous n'êtes pas dans une méthode async ou si vous utilisez une ancienne version du framework .Net .. utilisez simplement Tâches pour plus de simplicité:

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

Vous pouvez utiliser TPL pour attendre plusieurs tâches pendant leur exécution. Voir ici .

Comme ça:

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

En ajoutant aux autres réponses, vous pourriez faire quelque chose comme:

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
Dimitar Dimitrov