运行 异步代码与 task.run() 有什么好处

what's the benefit of running async code with task.run()

我很难理解将异步工作卸载到 Task.Run() 的好处。 之前有很多关于异步工作被卸载到任务的问题,但我从未见过,对我来说最大的谜团是什么,正在解决。

考虑两个选项:

var t1= Task.Run( async() =>
        {
            await CPUBoundWorkAsync();
        });
var t2= Task.Run( () =>
        {
             CPUBoundWork();
        });

使用第二个选项,工作被卸载到一个单独的线程,因此“实现”了异步性,并且主线程可以返回到它需要的任何地方。 对于第一个选项,使用 Async 方法并将委托表示为 Async。就不会阻塞主线程和同时完成 CPU 绑定工作而言,这两个选项应该会产生无法区分的结果。在 Task.Run 中使用 Async 方法有好处吗?在我看来,这 本质上是让已经在 运行 异步(在不同的非阻塞线程上)的工作也异步 运行 本身。 可以线程池或管理那些读入其中的任务线程的任何东西?然后也许给那个正在等待的线程做一些其他的工作?如果是这种情况,我还没有在任何专家博客 post 或互联网上的相关页面中看到它。

在您的示例中,如果 f1()f2() 是异步的(因为它们是 return 任务),那么 async 关键字将允许您等待 [=12= 中的那些].

您的问题类似于“为什么要将 any 方法标记为异步”……因为您希望在内部使用 运行 异步代码。或者更具体地说,您希望编译器构建状态机来处理“等待”异步操作完成。

我不确定我是否答对了你的问题。专注于 “它本质上是让已经 运行 异步的代码也异步 运行 本身”:

虽然在 Task.Run 中 运行 异步代码似乎毫无意义,但在某些情况下这可能是必要的。异步方法总是 运行 同步,直到第一个“真正的”异步调用发生。一个很好的例子是使用 backgroundservices.

With the 2nd option, the work is already being offloaded to a separate thread, then the asynchronicity is already gained in the scope of the program that invoked t2.

Task.Run 不会使某些东西异步。它确实允许您在线程池线程上 运行 同步代码(从而阻塞线程池线程而不是其他线程),但我不会说它“使其异步”。代码仍在同步阻塞线程。

Now although the work that is being done in the 1st option is declared and implemented as async, it's essentially letting code that is already being ran asynchronously, to also run itself asynchronously. What benefits from it ? Can the the scheduler or whatever manages those task threads read into this? and then perhaps give that awaiting thread some work on another task ?

线程没有 await。方法 await。当一个线程正在 运行 调用一个方法并遇到一个 await 时,该方法被暂停,方法 return 发送给它的调用者,线程继续执行。在委托传递给 Task.Run 的情况下,await 将导致方法 return -- 到线程池,因此 return 将线程发送到线程池.当 awaited 任务完成时,将使用线程池中的一个线程来恢复执行该方法;这可能是同一个线程或完全不同的线程。

有关 how exactly await works 的更多信息在我的博客上。

if this is the case I haven't seen it mentioned in any expert blog post, or relevant page on the internet.

我有一个关于 Task.Run etiquette 的系列。本质上,Task.Run 主要用于卸载同步或 CPU-bound 在 GUI 应用程序中关闭 UI 线程。如果您想快速开始处理某些内容并返回并获取下一个要处理的内容,它偶尔在 server-side 场景中也很有用。还有一些其他用例,但它们很少见。

这些原因都适用于异步 API。有时 API 看起来 是异步的,但实际上并非如此。或者某些方法是异步的,但它们首先执行大量同步工作 ,因此即使使用异步代码,在某些情况下 Task.Run 仍然是可取的。