C# 在同一查询中使用AsParallel()和AsSequential()的影响?C
我在其中一本书中读到了PLINQ,它说: 如果您有一个可以从并行处理中受益的复杂查询 但也有一些部分应该按顺序完成,您可以使用 用于停止并行处理查询的断言 例如:C# 在同一查询中使用AsParallel()和AsSequential()的影响?C,c#,.net,visual-studio,linq,plinq,C#,.net,Visual Studio,Linq,Plinq,我在其中一本书中读到了PLINQ,它说: 如果您有一个可以从并行处理中受益的复杂查询 但也有一些部分应该按顺序完成,您可以使用 用于停止并行处理查询的断言 例如: var parallelResult = numbers.AsParallel().AsOrdered() .Where(i => i % 2 == 0).AsSequential(); 我想了解为什么允许这样做,以及对结果有什么影响?它是并行运行的吗?它是按顺序运行的吗?现在没有任何意义。您可以将LINQ查询概念化为
var parallelResult = numbers.AsParallel().AsOrdered()
.Where(i => i % 2 == 0).AsSequential();
我想了解为什么允许这样做,以及对结果有什么影响?它是并行运行的吗?它是按顺序运行的吗?现在没有任何意义。您可以将LINQ查询概念化为具有单个执行计划的原子构造,但将is概念化为包含多个数据流块的管道可能更有帮助。每个块的输出成为数据流中下一个块的输入,这些块一可用就同时处理项目。以下一个查询为例,该查询由两个Select运算符表示的两个块组成。第一块配置为一次并行处理3个项目,而第二块配置为按顺序处理每个项目。对于并行块,每个项目的处理持续时间为1000毫秒,对于顺序块,每个项目的处理持续时间为500毫秒:
var results = Partitioner
.Create(Enumerable.Range(1, 10), EnumerablePartitionerOptions.NoBuffering)
.AsParallel()
.AsOrdered()
.WithDegreeOfParallelism(3)
.WithMergeOptions(ParallelMergeOptions.NotBuffered)
.Select(x =>
{
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff}"
+ $" [{Thread.CurrentThread.ManagedThreadId}] Parallel #{x}");
Thread.Sleep(1000); // Simulate some CPU-bound work
return x;
})
.AsSequential()
.Select(x =>
{
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff}"
+ $" [{Thread.CurrentThread.ManagedThreadId}] Sequential #{x}");
Thread.Sleep(500); // Simulate some CPU-bound work
return x;
})
.ToArray();
Console.WriteLine($"Results: {String.Join(", ", results)}");
如果运行此代码,将得到如下输出:
08:32:17.628[4]平行线2
08:32:17.628[5]平行线1
08:32:17.628[6]平行3
08:32:18.642[6]平行5
08:32:18.642[5]平行4
08:32:18.644[4]平行6
08:32:18.651[1]
08:32:19.644[6]平行线7
08:32:19.645[4]平行8
08:32:19.646[5]平行9
08:32:19.654[1]2
08:32:20.156[1]3
08:32:20.648[4]平行10
08:32:20.658[1]4
08:32:21.161[1]5
08:32:21.663[1]6
08:32:22.164[1]7
08:32:22.672[1]8
08:32:23.173[1]9
08:32:23.675[1]10
结果:1,2,3,4,5,6,7,8,9,10
请注意,在所有并行处理完成之前,顺序处理是如何开始的。为了达到这个效果,我使用了配置选项和,以防止第一个块缓冲其输入和输出
为了完整起见,让我们使用库重写此查询。代码变得更加冗长和流畅,但执行控制变得更加精确,异步工作流也变得可用PLINQ不支持异步:
var block1 = new TransformBlock<int, int>(async x =>
{
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff}"
+ $" [{Thread.CurrentThread.ManagedThreadId}] Parallel #{x}");
await Task.Delay(1000); // Simulate some I/O operation
return x;
}, new ExecutionDataflowBlockOptions()
{
MaxDegreeOfParallelism = 3,
EnsureOrdered = true // redundant since EnsureOrdered is the default
});
var block2 = new TransformBlock<int, int>(async x =>
{
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff}"
+ $" [{Thread.CurrentThread.ManagedThreadId}] Sequential #{x}");
await Task.Delay(500); // Simulate some I/O operation
return x;
}); // MaxDegreeOfParallelism = 1 is the default
block1.LinkTo(block2, new DataflowLinkOptions() { PropagateCompletion = true });
// Feeding the first block
foreach (var x in Enumerable.Range(1, 10))
{
await block1.SendAsync(x);
}
block1.Complete();
var results = new List<int>(); // Collecting the results is a bit painful
while (await block2.OutputAvailableAsync())
{
while (block2.TryReceive(out var result))
{
results.Add(result);
}
}
await block2.Completion;
Console.WriteLine($"Results: {String.Join(", ", results)}");
内存中的代码i%2==0,其中lambda将在多个线程中并行运行。这是你想要的吗?查询结束时的AsSequential没有影响,因为之后不会再进行计算。@TheodorZoulias我想知道将这两个方法调用在一起是否有好处。这是一个例子,以防。。。。例如,如果要获取数据,可以并行获取所有数据。现在这很好,而且比按顺序做要快。但有时你需要做更多的逻辑,一个接一个地评估每个值。例如,在订购过程中。您将无法执行此并行操作,因为您需要保留在查询的前一个子句中建立的顺序。关于AsParallel和AsSequential,也许你可以参考