哪个更有效率-Task.Run有2个等待中的I / O绑定任务,还是经典的Fork / Join方法?

考虑一下,对于N个元素,我们有2个I / O绑定任务需要处理。我们可以将这两个任务称为 A B B 只能在 A 产生结果之后运行。

我们可以通过两种方式完成此任务。 (请忽略访问修改后的闭包的情况。)

Task.Run方式:

List<Task> workers = new List<Task>();
for (int i = 0; i < N; i++)
{
    workers.Add(Task.Run(async () =>
    {
        await A(i);
        await B(i);
    }
}
await Task.WhenAll(workers);

经典Fork / Join:

List<Task> workersA = new List<Task>();
List<Task> workersB = new List<Task>();
for (int i = 0; i < N; i++)
{
    workersA.Add(A(i));
}

await Task.WhenAll(workersA);

for (int i = 0; i < N; i++)
{
    workersB.Add(B(i));
}

await Task.WhenAll(workersB);

或者,也可以通过以下方式完成此操作:

List<Task> workers = new List<Task>();

for (int i = 0; i < N; i++)
{
    workers.Add(A(i));
}

for (int i = 0; i < N; i++)
{
    await workers[i];
    workers[i] = B(i);
}

await Task.WhenAll(workers);

我担心的是following MSDN文档指出,我们永远不要对I / O操作使用Task.Run。

考虑到这一点,那么处理这种情况的最佳方法是什么?

如果我错了,请更正我,但是我们要避免使用Task.Run,​​因为我们有效地将线程排队以处理工作,如果我们仅使用await,则在那里won't be any thread。 (由于操作是I / O。)

我真的很想走Task.Run路线,但是如果它最终由于没有明显的原因而使用线程/增加了额外的开销,那么那就不行了。

randy8546 回答:哪个更有效率-Task.Run有2个等待中的I / O绑定任务,还是经典的Fork / Join方法?

  

我真的很想走Task.Run路线

为什么?

  

但是如果它最终由于没有明显原因而使用线程,那么这是不可行的。

The documentation说:

  

将指定的工作排队以在ThreadPool上运行

这不一定意味着每次您调用Task.Run时都有一个全新的线程。它可能但不一定。您可以保证的是,它将在不是当前线程的线程上运行。

您无法控制创建多少个线程来完成所有工作。但是建议不要对I / O操作使用Task.Run。毫无必要的不必要的开销。 效率会降低。

您的其他任何解决方案都可以正常工作。您的上一个解决方案可能会更快地完成,因为您将更快地开始呼叫B()(在开始呼叫A()之前,您只等待第一个B()完成,而不是等待所有呼叫完成) )。

根据Theodor的答案进行更新:我们都是正确的:)重要的是要知道异步方法中的所有代码在第一次等待之前(以及代码)之后,除非您另外指定),将在其开始时所在的上下文中运行。在桌面应用程序中,这就是UI线程。 等待是异步的。因此,UI线程在等待时 被释放。但是,如果该方法有大量占用CPU的工作,它将锁定UI线程。

因此,西奥多(Theodor)说,您可以使用Task.Run尽快将其移出UI线程,并确保它永远不会锁定UI线程。确实如此,您不能盲目使用该建议。首先,您可能需要在I / O操作之后在UI中执行某些操作,而必须必须在UI线程上完成。如果使用Task.Run运行它,则必须确保将其编组回UI线程。

但是,如果您调用的异步方法具有足够的CPU绑定工作,从而冻结了UI,则它并不是严格的I / O操作,建议“使用Task.Run进行CPU绑定工作,而{用于I / O的{1}} / async仍然合适。

我只能说:尝试。如果发现您所做的任何事情都会冻结UI,请使用await。如果发现不是这样,则Task.Run会造成不必要的开销(请注意,这没什么大不了,但仍然没有必要,但是如果像这样循环地执行,则会变得更糟)。

所有真正适用于桌面应用程序的内容。如果您使用的是ASP.NET,那么除非您尝试并行执行某项操作,否则Task.Run不会为您做任何事情。在ASP.NET中,没有“ UI线程”,因此在哪个线程上工作都没有关系。您只想确保在等待时不锁定线程(因为ASP.NET中的线程数量有限)。

,
  

如果您的工作是 I / O绑定,请使用asyncawait Task.Run。您不应使用任务并行库。 Async in Depth article中概述了其原因。

尽管it comes from the site of Microsoft这条建议令人误解。通过阻止Task.Run进行I / O操作,作者可能会想到以下几点:

var data = await Task.Run(() =>
{
    return webClient.DownloadString(url); // Blocking call
});

...这的确很糟糕,因为它会阻塞线程池线程。但是将Task.Run与异步委托一起使用是完全可以的:

var data = await Task.Run(async () =>
{
    return await webClient.DownloadStringTaskAsync(url); // Async call
});

实际上in my opinion是从UI应用程序的事件处理程序启动异步操作的首选方法,因为它可以确保UI线程将立即释放。相反,如果您遵循本文的建议并省略了Task.Run

private async void Button1_Click(object sender,EventArgs args)
{
    var data = await webClient.DownloadStringTaskAsync(url);
}

...然后您冒着异步方法可能不是100%异步的风险,并且可能会阻塞UI线程。对于像专家编写的DownloadStringTaskAsync这样的内置异步方法,这是一个很小的问题,但是对于第三方异步方法,它则是一个更大的关注,对于开发人员自己编写的异步方法,它甚至是一个更大的关注! / p>

因此,关于您的问题的选择,我认为第一个(Task.Run方法)是最安全,最有效的。第二个将分别等待所有A和所有B任务,因此持续时间最多为Max(A)+ Max(B)。统计上应该比Max(A + B)长。

本文链接:https://www.f2er.com/3136839.html

大家都在问