49

我有一种方法可以执行 2 个独立的逻辑。我希望我可以同时运行它们......并且只有在这两个子方法都完成后才能继续。

我试图弄清楚async/await语法,但我就是不明白。

这是代码:

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

因此,对于上面的代码,我想说:去同时获取所有的猫和食物。一旦我们完成,然后返回一个新的PewPew.

我很困惑,因为我不确定上面的哪些类是async或返回 aTask等。所有这些?只有两个私人的?我也猜想我需要利用该Task.WaitAll(tasks)方法,但我不确定如何设置任务以同时运行。

建议,好心人?

4

5 回答 5

63

以下是您可能想要做的事情:

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

这里有两点你需要了解:

1)这之间有什么区别:

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

还有这个:

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

两者都会给你类似的结果,让这两个async任务完成然后return new PewPew- 但是,不同之处在于Task.WaitAll()会阻塞当前线程(如果那是 UI 线程,那么 UI 将冻结)。相反,await它将在状态机中分解SomeMethodsay,并在遇到关键字时从 theSomeMethod调用者返回。await它不会阻塞线程。下面的代码await将安排在async任务结束时运行。

2)你也可以这样做:

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

但是,这不会async同时启动任务。第二个任务将在第一个任务结束后开始。这是因为await关键字的工作原理,希望对您有所帮助...

编辑:如何使用SomeMethod- 在调用树开始的某个地方,你必须使用Wait()orResult属性 - 或者 - 你必须awaitasync void. 通常,async void将是一个事件处理程序:

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

如果没有,则使用Result属性。

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

   return GetFoo2(p);
}
于 2013-05-24T06:15:19.850 回答
28

到目前为止,最简单的方法是使用Parallel.Invoke()

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

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

Parallel.Invoke()将在它本身返回之前等待所有方法返回。

更多信息在这里:http: //msdn.microsoft.com/en-us/library/dd460705.aspx

请注意,Parallel.Invoke()处理缩放到系统中处理器的数量,但这只有在您开始的不仅仅是几个任务时才真正重要。

于 2013-05-24T06:18:35.817 回答
12

如果您不在异步方法中,或者您使用的是旧版本的 .Net 框架,则不必使用异步。为简单起见,只需使用任务

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
于 2013-05-24T06:08:19.987 回答
1

您可以使用TPL在多个任务运行时等待它们。见这里

像这样:

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
               };
}
于 2013-05-24T06:09:50.237 回答
1

添加到其他答案,您可以执行以下操作:

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>();
}
于 2013-05-24T06:12:56.583 回答