【问题标题】:Refactoring Async/Await out for parallel processing重构 Async/Await 以进行并行处理
【发布时间】:2016-07-26 04:19:10
【问题描述】:

仍处于 C# 学习阶段,遇到了一个我需要帮助的问题。考虑以下代码:

private async Task<String> PrintTask()
{
    await Task.Delay(3000);
    return "Hello";
}

private async void SayHelloTwice()
{
    string firstHello = await PrintTask();
    string secondHello = await PrintTask();

    Console.WriteLine(firstHello);
    Console.WriteLine(secondHello);
}

现在 SayHelloTwice() 需要 6 秒才能完成。但是,我希望检索任务并行运行,这样只需 3 秒即可完成。我将如何重构我的代码来实现这一目标?谢谢!

【问题讨论】:

  • 只是对术语的评论:“并行”意味着多个线程。你想要的是异步并发,而不是并行

标签: c# asynchronous parallel-processing async-await task


【解决方案1】:

执行此操作的正确方法(没有死锁风险)是使用Task.WhenAll

private async void SayHelloTwice()
{
    string[] results = await Task.WhenAll(
        PrintTask(),
        PrintTask());

    Console.WriteLine(results[0]);
    Console.WriteLine(results[1]);
}

库编写者通常会努力使任务中的“回调”代码在调用它的原始线程上运行。这通常是因为对象只能从单个线程访问。

当使用 Task.ResultTask.WaitAll 等阻塞调用时,线程会暂停(不能做额外的工作)直到 Task 完成。

但如前所述,Task 通常会等待调用线程释放,因此可以使用它来完成任务。

所以,在第一种情况下,外部“任务”持有线程,正在等待完成。

第二种情况,内部的“任务”正在等待线程完成。

因此,两者都不会完成。

【讨论】:

  • 如果您要引入“死锁”的威胁,不妨解释一下何时何地这是一个问题。
  • 这个答案是正确的我只是觉得整个死锁解释是多余的,因为它与实际问题无关
  • 由于改进而撤回反对票,但同意 Itsik。我认为对死锁的关注是没有根据的,并且有可能将有用的建议变成无用且模糊的警告——尤其是因为这似乎是一个控制台应用程序(据我所知,阻塞通常不是问题)。跨度>
【解决方案2】:

一般来说,您想要启动这两个任务并在之后等待。 一个简单的方法是:

private async void SayHelloTwice()
{
    // Start the tasks, don't wait
    Task<string> firstHello = PrintTask();
    Task<string> secondHello = PrintTask();

    // Wait for results
    string firstResult = await firstHello;
    string secondResult = await secondHello;

    // Print results
    Console.WriteLine(firstResult);
    Console.WriteLine(secondResult);
}

这里发生的是对PrintTask() 的调用将开始执行该方法,一旦执行到达第一个执行实际异步操作的await,运行中的任务将被返回并分配给firstHello。 secondHello 也是如此。 然后等待两者都完成并打印结果。

此实现只是简化工作方式的示例。在实际代码中,您可能应该使用Task.WhenAll 来等待所有正在运行的任务

【讨论】:

  • 这与 OP 有什么不同?
  • 两个任务同时启动,而不是等待第一个任务完成后再运行第二个
  • 啊,我现在在 cmets 中看到了。谢谢。
【解决方案3】:
  • 您可以使用Task.WhenAll 等待多个任务。
  • 您还应该更喜欢返回 Task 而不是 void (async/await - when to return a Task vs void?):

    private static async Task SayHelloTwice()
    {
        var hellos = await Task.WhenAll(PrintTask(), PrintTask());
        Console.WriteLine(hellos[0]);
        Console.WriteLine(hellos[1]);
    }
    

【讨论】:

    猜你喜欢
    • 2019-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-14
    • 1970-01-01
    • 1970-01-01
    • 2017-12-17
    相关资源
    最近更新 更多