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

Posted

技术标签:

【中文标题】如何在 .NET 4.5 中“同时”运行这两种方法?【英文标题】:How can I run both of these methods 'at the same time' in .NET 4.5? 【发布时间】:2013-05-19 15:55:32 【问题描述】:

我有一个方法可以执行 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 或返回 Task 等等。全部?只有两个私人的?我也猜想我需要利用 Task.WaitAll(tasks) 方法,但我不确定如何设置任务同时运行。

建议,好心人?

【问题讨论】:

Foo foo 将在线程之间共享。确保您lock 正确。 【参考方案1】:

这是您可能想要做的事情:

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

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

2) 你也可以这样做:

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

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

编辑:如何使用SomeMethod - 在调用树的开始处,你必须使用Wait()Result 属性 - 或者 - 你必须从async void 使用await。 通常,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);

【讨论】:

await cTaskawait fTask 不会等待第一个然后是第二个吗?我的意思是它会并行吗? 是的,等待将是顺序的,但是,任务已并行启动。就等待时间而言,它与 task.WaitAll() 没有什么不同。 @YK1 soz 伙计——我很困惑。你是说即使你做了var cats = await cTask;var food = await fTask .. 这两个任务将同时运行(异步),而不是一次一个(同步)。跨度> @Pure.Krome 是的,当你说 Task.Run() 而不是当你说 await 时,任务将开始运行。 @YK1 我也得到一个编译错误:( The return type of an async method myst be void, Task or Task&lt;T&gt; .. 这是用于方法SomeMethod..【参考方案2】:

到目前为止,最简单的方法是使用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() 会根据系统中的处理器数量进行扩展,但这仅在您启动的任务不止几个时才真正重要。

【讨论】:

像Task.WaitAll()、Parallel.Invoke()会阻塞调用线程。这与等待不同。 @YK1 确实,但操作员问“我希望我可以同时运行它们......并且只有在这两个子方法都完成后才能继续。”这是当然的。 @YK1 是的,但是他不需要异步,而且您在上面的回答实际上并没有并行运行任务 - 它是按顺序运行的。 @YK1 这就是为什么像我说的那样使用Parallel.Invoke() 要容易得多。 @YKI1 这并不难理解(不要再开玩笑了!)。但是您的答案并未证明并行运行两个线程,同时还在 UI 中等待它而不会阻塞。无论如何,这就是我要说的。 :)【参考方案3】:

如果您没有使用异步方法,或者您使用的是旧版本的 .Net 框架,则不必使用异步。为简单起见,只需使用 Tasks: p>

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

【讨论】:

@AdamTal - 何时使用任务以及何时使用异步?两种方法是否同时运行? @zameeramir - 异步是运行任务的编译器实现。尽可能使用异步,但当您不在异步上下文中时,您可以简单地使用任务在不同的线程中运行。 @AdamTal 你的话太古怪了。你能简化它们吗? This entire page 让我绞尽脑汁 @student 我写得尽可能简单。如果仍然不清楚,您似乎会错过一些知识。尝试阅读 async 和 await (msdn.microsoft.com/en-us/library/hh191443.aspx) 和任务 (codeproject.com/Articles/189374/…) 无限运行如何阻止它们【参考方案4】:

除了其他答案,您可以执行以下操作:

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>();

【讨论】:

【参考方案5】:

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

像这样:

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
               ;

【讨论】:

以上是关于如何在 .NET 4.5 中“同时”运行这两种方法?的主要内容,如果未能解决你的问题,请参考以下文章

无论如何,这两种方法同时被调用,如何解决?

如何在运行时检测 .NET 4.5 版当前正在运行您的代码?

如何让 .NET 4.5 网站在 IIS6 上运行?

如何使用 Mono 在 XSP 上运行 .NET 4.5?

当 Fiddler 运行 .Net 4.5 时,WCF 服务正在工作

如何正确理解.NET 4.5和C#5.0中的async/await异步编程模式