【发布时间】:2014-09-18 11:41:41
【问题描述】:
我尝试使用 async-await 并行运行几种计算密集型方法。
我有一个包含大约 80,000 个对象的列表,我将这些对象输入到返回任务的函数中:
public static void Main(string[] args)
{
//...blah blah blah...
var runner = new Runner(); //in a nutshell, I manage to get an object that has an async method on it.
runner.Run().Wait(); //and I wait for it to complete.
//...blah blah blah...
}
我的跑步者对象中有以下方法(或多或少......这是一个人为的例子):
public async Task Run()
{
var items = ... //this is my list
var tasks = items.Select(i => this.RunItemAsync(i)).ToArray();
//I don't get here until the tasks are all finished...every single one...
await Task.WhenAll(tasks).ConfigureAwait(false);
}
private async Task RunItemAsync(Item i)
{
var subItems = i.GetSubItems();
var tasks = subItems.Select(s => s.RunSubItemAsync(s)).ToArray();
//I don't get here until the sub item tasks are all finished...
await Task.WhenAll(tasks).ConfigureAwait(false);
//does computations, doesn't wait on any async i/o, etc
await this.ProcessAsync(i).ConfigureAwait(false);
}
private async Task RunSubItemAsync(SubItem s)
{
//does computations, doesn't wait on any async i/o, etc
...
}
在过去一年左右的时间里,我一直在为异步等待而苦苦挣扎,有时使用 TPL Dataflow 实现了出色的性能并做出了一些非常酷的事情,但每隔一段时间我就会遇到这样的事情,而我就是做不到似乎让任务“激活”了它们的并行能力。这个特定的项目将在大约 16 个内核的服务器上运行,所以我真的很想利用它。我的开发虚拟机只分配了 2 个内核,但这仍应允许任务激活和并行运行(过去也是如此)。
我的观察
- 我设法通过在
RunItemAsync方法的开头插入一个小的await Task.Delay(1).ConfigureAwait(false)来并行运行。我知道这会创建某种形式的“喘息空间”,允许另一个任务使用线程。然而,这还不够,因为它肮脏、不可靠,并且需要我有不可接受的延迟。 - 没有前面提到的
Delay调用,所有任务都在Main Thread上运行。这对我来说很明显,因为Main是启动这一切的函数。我对此没有任何问题,但我过去曾遇到过experiences,在new Thread-created 线程上运行任务导致它无法使用默认任务调度程序运行,并且每个任务最终都在该线程上按顺序运行。也许Main Thread属于这一类?
我的问题
我了解运行 ToArray 本身不会执行异步代码。但是,我希望发生的是,当我的 RunItemAsync 方法到达其第一个 await 时,它将“停止”并允许调用 ToArray 的下一次迭代运行。
我也明白添加await Task.Delay 是有效的,因为它导致了我上面想要的结果。必须有某种方法可以做到这一点,而无需诉诸 await Task.Delay...
我怎样才能并行启动所有这些计算密集型任务,而不会无意中导致它们按顺序运行?
【问题讨论】:
-
您的意思是
items.Select(i => this.RunItemAsync(i)).ToArray()还是您没有提供的Run(Item)方法? -
已修复。这就是我中途重命名时发生的情况......
-
这真的取决于进度如何前进(一次一个,或并行),具体取决于 RunSubItemAsync 所做的事情。如果没有这方面的详细信息,很难准确说出需要更改的内容
-
它确实做了一堆数学运算。它遍历一个图,确定与
SubItem s表示的对象最近的节点,确定哪些节点最适合我正在计算的任务,执行一些无法异步获取的数据,对 @ 进行一些更改987654340@,然后退出。它完全同步。这种计算不能并行化,但我想做的是在不同的SubItems/Items 上并行运行许多这些计算。我遇到的问题是他们从不“释放”线程以便其他人可以执行,因此在完成之前没有其他任务开始。 -
当你调用它 RunSubItemAsync 时,它表明它不是同步的。
标签: c# parallel-processing async-await