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

时间:2018-09-08 10:58:36

标签: c# async-await task-parallel-library

在Task中,我有很多工作要做(CPU约束或IO约束,没有异步接口可使用)。我想知道是否可以在这样的任务中完成所有非异步工作:

async Task DoSomeStuff()
{
    await SomethingAsync();
    …
    DoCpuBoundWork();
    …
    await SomethingElseAsync();
}

还是应该使用Task.Run这样?

async Task DoSomeStuff()
{
    await SomethingAsync();
    …
    await Task.Run(() => DoCpuBoundWork());
    …
    await SomethingElseAsync();
}

我知道任务不一定要在另一个线程上执行,所以我想知道调度程序是否可能假设任务是非阻塞的,这将使Task之外的CPU绑定工作运行。 。例如,如果调度程序决定将CPU绑定的工作调度到应用程序的UI线程,则可能会降低速度。

我可以肯定已经问过这个问题了,但是我找不到合适的关键词来搜索,对此感到抱歉。

2 个答案:

答案 0 :(得分:1)

this page中所述(对于CPU绑定操作,深入研究Task和Task 部分),任务在调用它们的线程上运行,并且CPU绑定的工作确实应该是包装在Task.Run中,以便它将在另一个(后台)线程上运行。因此,是的,在异步方法中使用Task.Run进行CPU绑定或阻塞工作是正常的,这是正常的。从页面引用:

public async Task<int> CalculateResult(InputData data)
{
    // This queues up the work on the threadpool.
    var expensiveResultTask = Task.Run(() => DoExpensiveCalculation(data));

    // Note that at this point, you can do some other work concurrently,
    // as CalculateResult() is still executing!

    // Execution of CalculateResult is yielded here!
    var result = await expensiveResultTask;

    return result;
}
  

CalculateResult()在调用它的线程上执行。当它调用Task.Run时,它将在线程池上排队昂贵的CPU绑定操作DoExpensiveCalculation(),并接收一个Task句柄。 DoExpensiveCalculation()最终在下一个可用线程(可能在另一个CPU内核)上同时运行。在另一个线程上DoExpensiveCalculation()繁忙时,可以执行并发工作,因为调用CalculateResult()的线程仍在执行。

     

一旦遇到等待,就会将CalculateResult()的执行交给其调用方,从而允许在DoExpensiveCalculation()产生结果时使用当前线程完成其他工作。完成后,结果将排队等待在主线程上运行。最终,主线程将返回执行CalculateResult(),这时它将获得DoExpensiveCalculation()的结果。

答案 1 :(得分:0)

答案是:这取决于。

您指出了调度程序,这就是问题所在。如果您正在线程池(默认调度程序)上运行,那么运行同步的CPU绑定工作就可以了。如果您正在UI线程上运行,则可能会导致不良的用户体验。

也就是说,虽然这是一个问题,但这不是您的问题。通过实施DoSomeStuff,您的合同是您将在当前调度程序上运行一些工作。由调用者确定这是否可能是一个问题,在这种情况下,请添加Task.Run以将调用偏移到另一个调度程序。

简而言之,不要使用await Task.Run(() => DoCpuBoundWork());。让知道执行上下文的调用者为您决定。