【问题标题】:Combining PLINQ with Async method将 PLINQ 与 Async 方法相结合
【发布时间】:2017-04-28 19:40:51
【问题描述】:

我正在尝试像这样组合我的 PLINQ 语句:

Enumerable.Range(0, _sortedList.Count()).AsParallel().WithDegreeOfParallelism(10)
          .Select(i =>  GetTransactionDetails(_sortedList[i].TransactionID))
          .ToList();

使用这样的异步方法:

 private async void GetTransactionDetails(string _trID)
 {
      await Task.Run(() =>
      {
      });
 }

这样我就可以在这里简单地添加一个等待操作符:

 Enumerable.Range(0, _sortedList.Count()).AsParallel().WithDegreeOfParallelism(10)
           .Select(i => await GetTransactionDetails(_sortedList[i].TransactionID))
           .ToList();

我怎样才能做到这一点?

附:这样我可以同时发出 5-10 个 HTTP 请求,同时确保最终用户在这样做时不会感觉到任何“屏幕”冻结...

【问题讨论】:

  • 你为什么要尝试并行化异步操作的启动,因为启动它基本上需要零时间。 PLINQ 用于长时间运行的 CPU 绑定操作;那不是你所拥有的。此外,看起来GetTransactionDetails 正在使用异步上同步反模式。它不应该将工作卸载到另一个线程,而应该只是一个同步方法。如果调用者想用Task.Run 调用它,他们可以,如果他们想做其他事情,比如使用 PLINQ,那么他们可以这样做。
  • @Servy 你能告诉我如何在一个更实际的例子中实现你刚才所说的吗?
  • GetTransactionDetails 中删除Task.Run,让它同步工作,从而允许PLINQ 并行化它。
  • 哦,那样......我真的不能这样做,因为我在 10-15 分钟内向 PayPal 发出了大约 800 个请求,而这样做时,应用程序完全冻结了.. :/在执行 PLINQ 语句时有什么方法可以避免应用程序冻结?
  • 如果正在做的工作实际上是网络请求,那么你根本不应该使用Task.Run,请求本质上应该是Task返回,根本没有理由使用 PLINQ,由于您没有想要同步执行的 CPU 操作,您应该简单地 await 您拥有的网络请求。

标签: c# winforms asynchronous c#-4.0 plinq


【解决方案1】:

您可以采取几种方法。

首先,“更正确”的方法(这也是更多的工作)。将GetTransactionDetails 变成正确的async 方法(即不使用Task.Run):

private async Task GetTransactionDetailsAsync(string _trID);

然后你可以同时调用那个方法:

var tasks = _sortedList.Select(x => GetTransactionDetailsAsync(x.TransactionID));
await Task.WhenAll(tasks);

如果您需要限制并发,请使用SemaphoreSlim

第二种方法更浪费(在线程使用方面),但考虑到我们已经看到的代码部分,可能更容易。第二种方法是让 I/O 保持同步,并以常规 PLINQ 方式进行:

private void GetTransactionDetails(string _trID);

_sortedList.AsParallel().WithDegreeOfParallelism(10).Select(x => GetTransactionDetails(x.TransactionID)).ToList();

为避免阻塞 UI 线程,您可以将其包装在单个 Task.Run 中:

await Task.Run(() => _sortedList.AsParallel().WithDegreeOfParallelism(10).Select(x => GetTransactionDetails(x.TransactionID)).ToList());

【讨论】:

  • 显然是惊人的回复,谢谢! await Task.Run() 正是我在这种情况下所需要的......虽然我不得不使用 Parallel.For 与 Parallel.For 循环的组合,而不是你在示例中显示的循环,因为 .Select 方法没有不能使用 void 方法,至少编译器是这样显示的?
  • 我有受 CPU 限制的异步方法,这些方法执行少量 IO,但需要针对受 CPU 限制的东西进行并行化。它使用的接口没有同步 API。我所做的是Task.WhenAll(items.Select(item => Task.Run(async () => { /* method which awaits */ })))。 Task.Run 用于处理在第一次等待之前发生的 CPU 密集型工作。一次排队到线程池似乎是迄今为止最快的。
  • 遇到@jnm2 的用例(CPU 绑定工作)的人应该查看PLINQ
  • @miniBill PLINQ 的问题在于它不适用于 CPU-bound 和间歇性 IO 限制,这是我的用例。它会强制你阻塞异步 IO。
猜你喜欢
  • 2018-07-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-04
  • 2021-12-15
  • 1970-01-01
相关资源
最近更新 更多