并行运行方法?

时间:2019-01-10 06:45:49

标签: c# asynchronous

以下代码用于模拟耗时的工作。

async Task DoWork(string n)
{
    for (var i = 1; i <= 5; i++)
    {
        System.Threading.Thread.Sleep(1000);
        Console.WriteLine($"{n} runs {i} seconds. {DateTime.Now}");
    }
}
async Task<int> F1()
{
    await DoWork("f1");
    return 1;
}
async Task<string> F2()
{
    await DoWork("f2");
    return "X";
}

以下代码将依次运行F1()F2()

async Task<string> Main()
{
    var f1Task = F1();
    var f2Task = F2();
    var f1 = await f1Task;
    var f2 = await f2Task;
    return $"{f1} {f2}";
}
await Main();

以下代码可使它们并行运行。但是,它看起来很麻烦。

async Task<string> Main2()
{
    int f1 = 0;
    async Task G1() { f1 = await F1(); }
    string f2 = "";
    async Task G2() { f2 = await F2(); }

    await Task.WhenAll(Task.Run(() => G1()), Task.Run(() => G2()));
    return $"{f1} {f2}";
}

await Main2();

有没有一种方法可以不将它们包装在Task.Run()中?

4 个答案:

答案 0 :(得分:0)

您可以使用WhenAll()方法使任务像

一样并行运行
async Task<int> Main()
{
    var f1Task = F1();
    var f2Task = F2();
    await Task.WhenAll(f1Task,f2Task);
    return await f1Task + await f2Task;
}

答案 1 :(得分:0)

await本身不会启动异步操作。 用这样的任务开始DoWork:

    async Task DoWork(string n)
    {
        await Task.Run(() => {
            for (var i = 1; i <= 5; i++)
            {
                System.Threading.Thread.Sleep(1000);
                Console.WriteLine($"{n} runs {i} seconds. {DateTime.Now}");
            }
        });
    }

然后您可以执行以下操作:

   {
        var f1Task =  F1();
        var f2Task =  F2();

        return await f1Task + await f2Task;
    }

答案 2 :(得分:0)

您说对了,看起来很麻烦。但是,它可以以一种简洁的方式完成。您可以创建一个任务数组,然后使用 WhenAll 一次等待所有任务。

var tasks = new List<Task<int>> { F1(), F2() };
var result = Task.WhenAll(tasks).GetAwaiter().GetResult();

唯一的条件是所有任务必须返回相同的结果类型。那么在这种情况下的结果是 int 的数组。

我写了一篇很好的关于并行处理的文章,您可以看看当任务是REST调用时如何完成此工作:http://www.michalbialecki.com/2018/04/19/how-to-send-many-requests-in-parallel-in-asp-net-core/

答案 3 :(得分:0)

  

这会增加io和CPU的负担。

如果只有I / O(即await Task.Delay而不是Thread.Sleep),则可以使用异步并发:

var task1 = F1();
var task2 = F2();
await Task.WhenAll(task1, task2);
int f1 = await task1;
string f2 = await task2;

但是,由于您也具有CPU(并且您的方法是同步的),因此您需要将其推送到另一个线程并使其并行。

  

有没有一种方法可以不将它们包装在Task.Run()中?

是的,但是Task.Run是这里最简单的解决方案。它看起来不像您的示例那么麻烦:

var task1 = Task.Run(() => F1());
var task2 = Task.Run(() => F2());
await Task.WhenAll(task1, task2);
int f1 = await task1;
string f2 = await task2;

从技术上讲,Task.WhenAll在这两个示例中都是可选的,但我喜欢它,因为它使语义更加清晰。