猿问

应该在另一个任务中使用 Task.Run 吗?

我有一堆工作要做(CPU 密集型或 IO 密集型,没有异步接口可用)。我想知道是否可以像这样在任务中完成所有非异步工作:


async Task DoSomeStuff()

{

    await SomethingAsync();

    …

    DoCpuBoundWork();

    …

    await SomethingElseAsync();

}

或者我应该像这样使用 Task.Run 吗?


async Task DoSomeStuff()

{

    await SomethingAsync();

    …

    await Task.Run(() => DoCpuBoundWork());

    …

    await SomethingElseAsync();

}

我知道任务不一定在另一个线程上执行,所以我想知道调度程序是否会假设任务是非阻塞的,这将使在 Task.Run 之外执行 CPU 密集型工作减慢应用程序的速度。例如,如果调度程序决定将受 CPU 限制的工作调度到应用程序的 UI 线程,则可能会变慢。


我确定以前有人问过这个问题,但是我找不到合适的关键字来搜索它,对此感到抱歉。


慕的地6264312
浏览 177回答 2
2回答

慕勒3428872

正如指出的这个页面(部分更加深入地了解任务和任务<T>的CPU绑定的操作),任务从他们被称为线程上运行,而CPU限制的工作其实应该被包裹在一个Task.Run如此它”将在另一个(后台)线程上运行。所以,是的,在异步方法中使用 Task.Run 来进行 CPU 绑定或其他阻塞工作是正常且正常的。从页面引用:public async Task<int> CalculateResult(InputData data){&nbsp; &nbsp; // This queues up the work on the threadpool.&nbsp; &nbsp; var expensiveResultTask = Task.Run(() => DoExpensiveCalculation(data));&nbsp; &nbsp; // Note that at this point, you can do some other work concurrently,&nbsp; &nbsp; // as CalculateResult() is still executing!&nbsp; &nbsp; // Execution of CalculateResult is yielded here!&nbsp; &nbsp; var result = await expensiveResultTask;&nbsp; &nbsp; return result;}CalculateResult() 在调用它的线程上执行。当它调用 Task.Run 时,它会将昂贵的 CPU 绑定操作 DoExpensiveCalculation() 排入线程池并接收一个任务句柄。DoExpensiveCalculation() 最终在下一个可用线程上并发运行,可能在另一个 CPU 内核上。当 DoExpensiveCalculation() 忙于另一个线程时,可以进行并发工作,因为调用 CalculateResult() 的线程仍在执行。一旦遇到 await,CalculateResult() 的执行就交给它的调用者,允许在 DoExpensiveCalculation() 生成结果时使用当前线程完成其他工作。完成后,结果将排队等待在主线程上运行。最终,主线程将返回执行CalculateResult(),此时它将得到DoExpensiveCalculation() 的结果。

冉冉说

答案是:视情况而定。您指出了调度程序,这正是问题所在。如果您在线程池(默认调度程序)上运行,那么运行同步的 CPU 密集型工作非常好。如果你在 UI 线程上运行,这可能会导致糟糕的用户体验。也就是说,虽然这是一个问题,但这不是你的问题。您的合同已实施DoSomeStuff,您将在当前调度程序上运行一些工作。由调用者决定这是否可能是一个问题,在这种情况下添加一个Task.Run以抵消对另一个调度程序的调用。简而言之,不要使用await Task.Run(() => DoCpuBoundWork());.&nbsp;让知道执行上下文的调用者为您决定。
随时随地看视频慕课网APP
我要回答