如何在.NET 4.5中"同时"运行这两种方法?

Pur*_*ome 39 .net c# task multitasking async-await

我有一个方法,它可以完成两个独立的逻辑.我希望我可以运行他们都在同一时间 ..当这两个孩子的方法完成事后才继续.

我试图弄清楚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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

建议,善良的人?

YK1*_*YK1 51

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

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;
}
Run Code Online (Sandbox Code Playgroud)

这里有两件事你需要理解:

1)这之间有什么区别:

var cats = await cTask;
var food = await fTask;
Run Code Online (Sandbox Code Playgroud)

还有这个:

Task.WaitAll(new [] {cTask, fTask});
Run Code Online (Sandbox Code Playgroud)

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

2)你也可以这样做:

var cats = await Task.Run(() => GetAllTheCats(foo));
var food = await Task.Run(() => GetAllTheFood(foo));
Run Code Online (Sandbox Code Playgroud)

但是,这不会async同时启动任务.第二项任务将在第一项任务结束后开始.这是因为await关键字如何工作,希望有帮助......

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

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

如果没有,那么使用Result财产.

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

   return GetFoo2(p);
}
Run Code Online (Sandbox Code Playgroud)

  • `await cTask`和`await fTask`不会等到第一个然后第二个?我的意思是它会平行吗? (2认同)
  • 是的,等待将是顺序的,但是,任务已经并行启动.它与task.WaitAll()在等待时间方面没有什么不同. (2认同)
  • @ YK1 soz哥们 - 我很困惑.你是说,即使你做`var cats = await cTask;`和`var food = await fTask` ..这两个任务将*同时运行*(async)而不是一次 - (一次) - 同步). (2认同)
  • @ Pure.Krome是的,任务将在你说Task.Run()时开始运行,而不是当你说等待时. (2认同)
  • @ YK1我也得到一个编译错误:(`async方法的返回类型myst是void,Task或Task <T>`..这是针对方法`SomeMethod` .. (2认同)
  • @ Pure.Krome:将其更改为Task <PewPew> (2认同)
  • 然后打破我对此方法的所有现有调用. (2认同)
  • 实际上,它不是很合乎逻辑 - 但原因在于:http://stackoverflow.com/questions/7010904/why-return-type-of-async-must-be-void-task-or-taskt (2认同)
  • 在调用树开始的某处,您必须接受任务并使用Result属性而不是await.或者 - 您必须等待异步空格. (2认同)
  • @ YK1我刚刚尝试过,它们按顺序运行,当然. (2认同)
  • @DimitarDimitrov:你在Task.Run()上尝试等待任务变量或等待?仔细阅读我的回答.此外,您可以在两个等待和任务之间添加Console.WriteLine. (2认同)
  • @ YK1`等待cTask,等待fTask`(一个接一个),第一个完成取消任务,第二个开始.也许我做错了什么,不确定. (2认同)
  • 我不知道为什么这被标记为答案.它甚至没有编译.如果您将其更改为正确编译,它将按顺序运行任务,而不是并行运行. (2认同)
  • @MatthewWatson假设我们正在查看相同的版本,那么它对我编译很好并且应该并行执行. (2认同)
  • @svick我试过的是第(2)部分"你也可以这样做",这似乎按顺序运行.我最后通过添加所有缺少的类来完成编译的第一部分,并且*确实*工作. (2认同)
  • @MatthewWatson是的,但答案明确指出该部分不会并行运行. (2认同)
  • @svick是的,是的; 我没有仔细阅读它!这确实是一个正确的答案.:) (2认同)
  • @svick:谢谢你的澄清和upvote (2认同)

Mat*_*son 20

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

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

Parallel.Invoke
(
    () => cats = GetAllTheCats(foo),
    () => food = GetAllTheFood(foo)
);
Run Code Online (Sandbox Code Playgroud)

Parallel.Invoke() 将等待所有方法返回之前返回.

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

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

  • @YK1 是的,但他不需要异步,并且您上面的回答实际上并没有并行运行任务 - 然后依次运行。 (2认同)
  • @YK1这就是为什么像我说的那样使用`Parallel.Invoke()`要容易得多。 (2认同)
  • @YKI1 不难理解(不要再开玩笑了!)。但是您的答案并没有展示并行运行两个线程,同时也在 UI 中等待它而不会阻塞。无论如何,这就是我要说的。:) (2认同)

Ada*_*Tal 10

如果您不使用异步方法或者使用旧版本的.Net框架,则不必使用async.只需使用Tasks来简化:

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
Run Code Online (Sandbox Code Playgroud)

  • +1混淆似乎是使用任务与异步. (2认同)