我有3个任务:

private async Task<Cat> FeedCat() {}
private async Task<House> SellHouse() {}
private async Task<Tesla> BuyCar() {}

它们都需要在我的代码继续之前运行,我也需要每个结果。这些结果之间没有任何共同之处

我如何调用和等待3个任务完成,然后得到结果?


当前回答

提出警告

对于那些访问这个线程和其他类似线程寻找使用async+await+task工具集并行化EntityFramework的方法的人来说,这里显示的模式是合理的,然而,当涉及到EF的特殊雪花时,除非在每个* async()调用中使用单独的(新的)db-context-instance,否则无法实现并行执行。

这类事情是必要的,因为ef-db-context固有的设计限制禁止在同一个ef-db-context实例中并行运行多个查询。


利用已经给出的答案,这是确保你收集所有值的方法,即使在一个或多个任务导致异常的情况下:

  public async Task<string> Foobar() {
    async Task<string> Awaited(Task<Cat> a, Task<House> b, Task<Tesla> c) {
        return DoSomething(await a, await b, await c);
    }

    using (var carTask = BuyCarAsync())
    using (var catTask = FeedCatAsync())
    using (var houseTask = SellHouseAsync())
    {
        if (carTask.Status == TaskStatus.RanToCompletion //triple
            && catTask.Status == TaskStatus.RanToCompletion //cache
            && houseTask.Status == TaskStatus.RanToCompletion) { //hits
            return Task.FromResult(DoSomething(catTask.Result, carTask.Result, houseTask.Result)); //fast-track
        }

        cat = await catTask;
        car = await carTask;
        house = await houseTask;
        //or Task.AwaitAll(carTask, catTask, houseTask);
        //or await Task.WhenAll(carTask, catTask, houseTask);
        //it depends on how you like exception handling better

        return Awaited(catTask, carTask, houseTask);
   }
 }

具有或多或少相同性能特征的替代实现可以是:

 public async Task<string> Foobar() {
    using (var carTask = BuyCarAsync())
    using (var catTask = FeedCatAsync())
    using (var houseTask = SellHouseAsync())
    {
        cat = catTask.Status == TaskStatus.RanToCompletion ? catTask.Result : (await catTask);
        car = carTask.Status == TaskStatus.RanToCompletion ? carTask.Result : (await carTask);
        house = houseTask.Status == TaskStatus.RanToCompletion ? houseTask.Result : (await houseTask);

        return DoSomething(cat, car, house);
     }
 }

其他回答

你可以使用Task。如前所述,WhenAll或Task。WaitAll,这取决于您是否希望线程等待。看看这两个解释的链接。

WaitAll vs WhenAll

只需要分别等待这三个任务,在启动它们之后:

var catTask = FeedCat();
var houseTask = SellHouse();
var carTask = BuyCar();

var cat = await catTask;
var house = await houseTask;
var car = await carTask;

注意:如果任何一个任务抛出异常,这段代码可能会在后面的任务完成之前返回异常,但它们都将运行。在几乎所有的情况下,当你已经知道结果是理想的时候就不要等待。在边缘情况下,可能不是这样。

The three tasks in your example differ greatly in importance. In case one of them fails, you probably want to know what happened with the others. For example in case the communication with the automatic cat feeder failed, you don't want to miss whether selling your house succeeded or failed. So it makes sense to return back not just a Cat, a House and a Tesla, but the tasks themselves. The calling code will then be able to query separately each of the three tasks, and react appropriately to their successful or failed completions:

public async Task<(Task<Cat>, Task<House>, Task<Tesla>)> FeedCatSellHouseBuyCar()
{
    Task<Cat> task1 = FeedCat();
    Task<House> task2 = SellHouse();
    Task<Tesla> task3 = BuyCar();

    // All three tasks are launched at this point.

    try { await Task.WhenAll(task1, task2, task3).ConfigureAwait(false); } catch { }

    // All three tasks are completed at this point.
    
    return (task1, task2, task3);
}

使用的例子:

var (catTask, houseTask, teslaTask) = await FeedCatSellHouseBuyCar();

// All three tasks are completed at this point.

if (catTask.IsCompletedSuccessfully)
    Console.WriteLine($"{catTask.Result.Name} is eating her healthy meal.");
else
    Console.WriteLine("Your cat is starving!");

if (houseTask.IsCompletedSuccessfully)
    Console.WriteLine($"Your house at {houseTask.Result.Address} was sold. You are now rich and homeless!");
else
    Console.WriteLine("You are still the poor owner of your house.");

if (teslaTask.IsCompletedSuccessfully)
    Console.WriteLine($"You are now the owner a battery-powered {teslaTask.Result.Name}.");
else
    Console.WriteLine("You are still driving a Hyundai.");

带有空catch的try块是必需的,因为. net 7仍然没有提供适当的方法来等待任务,而不会在取消或失败时抛出。

你可以把它们存储在任务中,然后等待它们:

var catTask = FeedCat();
var houseTask = SellHouse();
var carTask = BuyCar();

await Task.WhenAll(catTask, houseTask, carTask);

Cat cat = await catTask;
House house = await houseTask;
Car car = await carTask;
var dn = await Task.WhenAll<dynamic>(FeedCat(),SellHouse(),BuyCar());

如果你想访问Cat,你可以这样做:

var ct = (Cat)dn[0];

这是非常简单的做法和非常有用的使用,没有必要去追求一个复杂的解决方案。