async不应该用于高CPU任务吗?

use*_*000 23 .net c# asynchronous async-await

我想知道是否真的async- await不应该用于"高CPU"任务.我在演讲中看到了这一点.

所以我猜这意味着什么

Task<int> calculateMillionthPrimeNumber = CalculateMillionthPrimeNumberAsync();
DoIndependentWork();
int p = await calculateMillionthPrimeNumber;
Run Code Online (Sandbox Code Playgroud)

我的问题是可以在上面是合理的,如果没有,有没有做一个高CPU任务异步的一些其他的例子吗?

EJo*_*ica 9

事实上,async/await有两个主要用途.一个(我的理解是,这是它被放入框架的主要原因之一)是使调用线程在等待结果时执行其他工作.这主要用于I/O绑定任务(即主要"持有"是某种I/O的任务 - 等待硬盘驱动器,服务器,打印机等响应或完成其任务).

作为旁注,如果你以这种方式使用async/await,重要的是确保你已经实现了它,使得调用线程在等待结果时实际上可以做其他工作; 我见过很多人做"A等待B,等待C"的事情; 这可能最终表现不会比A同时调用B同步而B只是同步调用C(因为调用线程在等待B和C的结果时从不允许做其他工作).

在I/O绑定任务的情况下,创建额外的线程只是为了等待结果是没有意义的.我在这里通常的比喻是考虑在一个有10人的餐馆中订购.如果服务员要求订购的第一个人还没有准备好,那么服务员不会等待他做任何其他人的订单之前做好准备,他也不会带第二个服务员等待第一个人.在这种情况下,最好的办法是询问小组中的其他9人的订单; 希望,到他们订购时,第一个人就会准备好.如果没有,至少服务员仍然节省了一些时间,因为他花了更少的时间闲着.

也可以使用像Task.RunCPU一样的任务(这是第二次使用).按照我们上面的类比,这是一个案例,通常有更多的服务员是有用的 - 例如,如果一个服务员有太多的桌子供服务.实际上,这实际上是"幕后"的所有内容都是使用线程池; 它是执行CPU绑定工作的几种可能结构之一(例如,只是将它"直接"放在线程池上,显式创建一个新线程,或使用后台工作程序)所以这是一个设计问题,你最终会使用哪种机制.

async/await这里的一个优点是它可以(在适当的情况下)减少必须手动编写的显式锁定/同步逻辑的数量.这是一个愚蠢的例子:

private static async Task SomeCPUBoundTask()
    {
        // Insert actual CPU-bound task here using Task.Run
        await Task.Delay(100);
    }

    public static async Task QueueCPUBoundTasks()
    {
        List<Task> tasks = new List<Task>();

        // Queue up however many CPU-bound tasks you want
        for (int i = 0; i < 10; i++)
        {
            // We could just call Task.Run(...) directly here
            Task task = SomeCPUBoundTask();

            tasks.Add(task);
        }

        // Wait for all of them to complete
        // Note that I don't have to write any explicit locking logic here,
        // I just tell the framework to wait for all of them to complete
        await Task.WhenAll(tasks);
    }
Run Code Online (Sandbox Code Playgroud)

显然,我在这里假设任务是完全可并行化的.另外请注意,您可以在这里自己使用线程池,但这样会有点不太方便,因为您需要某种方法来弄清楚自己是否所有这些都已完成(而不仅仅是让框架弄明白这一点)为了你).您也可以在Parallel.For这里使用循环.


Ste*_*ary 9

我想知道async-await是否应该用于"高CPU"任务.

是的,这是真的.

我的问题是上述情况是否合理

我会说这是没有道理的.在一般情况下,您应该避免使用Task.Run具有异步签名的方法.不要为同步方法公开异步包装器.这是为了防止消费者混淆,特别是在ASP.NET上.

然而,没有什么错误使用Task.Run调用一个同步的方法,例如,在UI应用程序.通过这种方式,您可以使用多线程(Task.Run)来保持UI线程的自由,并使用以下方式优雅地使用它await:

var task = Task.Run(() => CalculateMillionthPrimeNumber());
DoIndependentWork();
var prime = await task;
Run Code Online (Sandbox Code Playgroud)