是否有PLINQ的异步版本?

时间:2014-01-16 18:27:40

标签: .net asynchronous task-parallel-library plinq

我希望对数据流执行查询,同时以一定程度的并行性并行处理项目。通常情况下,我会使用PLINQ,但我的工作项不是CPU绑定的,而是IO绑定的。我想使用异步IO。 PLINQ不支持异步工作。

运行PLINQ样式查询的最智能方法是什么,但使用异步工作项?


以下是问题的更详细说明:

我的目标是通过以下查询逻辑描述的方式处理潜在的无限“项目”流:

var items = new int[10]; //simulate data

var results =
 from x in items.AsParallel().WithDegreeOfParallelism(100)
 where Predicate(x)
 select ComputeSomeValue(x);

foreach (var result in results)
 PerformSomeAction(result);

此查询只是真实查询的草图。现在我希望每个占位符函数都是异步(返回Task并在内部基于异步IO)。

请注意,可能存在的内容远远多于内存中可存储的内容。我还必须控制并行度以最大化底层网络和磁盘硬件。

这个问题不是关于多核的。它完全适用于只有一个CPU内核的机器,因为IO仍然可以从并行性中受益。想想慢速的网络服务电话等。

2 个答案:

答案 0 :(得分:5)

这听起来像是微软反应框架的工作。

我从这段代码开始作为我的初始变量:

var items = Enumerable.Range(0, 10).ToArray();

Func<int, bool> Predicate = x => x % 2 == 0;

Func<int, int> ComputeSomeValue = x =>
{
    Thread.Sleep(10000);
    return x * 3;
};

现在,我使用常规LINQ查询作为基线:

var results =
    from x in items
    where Predicate(x)
    select ComputeSomeValue(x);

这需要50秒来计算以下结果:

enumerable

然后我切换到一个可观察的(反应式框架)查询:

var results =
    from x in items.ToObservable()
    where Predicate(x)
    from y in Observable.Start(() => ComputeSomeValue(x))
    select y;

这需要10秒钟才能获得:

observable

显然是并行计算。

然而,结果出了问题。所以我将查询更改为:

var query =
    from x in items.ToObservable()
    where Predicate(x)
    from y in Observable.Start(() => ComputeSomeValue(x))
    select new { x, y };

var results =
    query
        .ToEnumerable()
        .OrderBy(z => z.x)
        .Select(z => z.y);

仍然需要10秒钟,但我按正确的顺序恢复了结果。

现在,这里唯一的问题是WithDegreeOfParallelism。这里有一些东西可以尝试。

首先,我将代码更改为生成10,000个值,计算时间为10ms。我的标准LINQ查询仍然需要50秒。但反应性查询耗时6.3秒。如果它可以同时执行所有计算,它应该花费更少。这表明它正在最大化异步管道。

第二点是反应式框架使用调度程序进行所有工作调度。您可以尝试使用反应式框架附带的各种调度程序,以便在内置程序不能满足您的需求时找到替代方案。或者您甚至可以编写自己的调度程序来执行您喜欢的任何调度。


这是一个同时计算谓词的查询版本。

var results =
    from x in items.ToObservable()
    from p in Observable.Start(() => Predicate(x))
    where p
    from y in Observable.Start(() => ComputeSomeValue(x))
    select new { x, y };

答案 1 :(得分:1)

如上所述here PLINQ 用于在多核/多处理器系统上并行运行 LINQ查询。对于具有大量磁盘单元和超级网络功能的酷系统,没什么好处。 AFAIK,它用于在更多内核上运行可执行代码,而不是同时向操作系统分派多个I / O请求。

也许您的 Predicate(x)受CPU限制,因此您可以使用PLINQ执行该过滤操作。但是,您无法以相同的方式应用I / O要求操作( ComputeSomeValue PerformSomeAction )。

您可以做的是为每个项目定义一个操作链(在您的情况下为两个)(请参阅continuation tasks)并调度该链(顺序(?))。

此外,您已经提到过“无限的项目流”。这可能听起来像生产者 - 消费者问题 - 如果这些项目也是I / O生成的话。

也许你的问题不是多核友好... 它可能只是I / O要求,这就是......