从 PLINQ Select 块中突破 (C#)
Breaking out from PLINQ Select Block (C#)
有什么方法可以从 PLINQ 的 SELECT 块中突破吗?基于 SELECT 块中的条件,我想打破循环。到目前为止,我看到了各种使用取消令牌从循环外部中断 PLINQ 的示例。但是,我的问题是从循环内部中断。
这是我的代码:
var simulationResults =
Enumerable.Range(0, 10000)
.AsParallel()
.WithCancellation(token ?? CancellationToken.None)
.Select(z =>
{
progressAction?.Invoke();
double aResult = someMethod()
if (double.IsNaN(aResult))
{
// todo: Find a way to Break the Parallel loop.
}
return aResult;
});
如果没有可用的技术,也许我需要找到一种困难的方法,比如使用带有 volatile 关键字的布尔标志。我将在 PLINQ Select 块中使用 lock 语句设置该标志,然后下一次,它将读取该标志并确定它是否应该中断。但是,不确定这是否是一个好的解决方案。此外,我非常不愿意在 PLINQ 内部使用锁,这违背了并行计算的目的。使用 Lock,我可能会强制执行同步而无法获得并行编程的好处。
那么有更好的主意吗?
我建议你使用 Parallel.For
而不是 PLINQ
:
Why not PLINQ?
Even though PLINQ provides support for exiting from
within a query execution, the differences between PLINQ’s exit
mechanism and Parallel.ForEach are substantial. To exit from a PLINQ
query, supply the query with a cancellation token, as explained here.
With Parallel.ForEach, exiting flags are polled on every iteration. In
PLINQ, the cancellation token is polled once every so often, so you
cannot assume that a cancelled query will stop quickly.
取自退出操作部分When To Use Parallel.ForEach and When to Use PLINQ。
在上面的文章中,您可以找到有关如何使用 ParallelLoopState
的 Stop
或 Break
方法停止 Parallel.ForEach
/Parallel.For
的详细说明,这些方法是那里有完整的描述 here:
In this context, "break" means complete all iterations on all threads
that are prior to the current iteration on the current thread, and
then exit the loop. "Stop" means to stop all iterations as soon as
convenient.
使用 Parallel.For
你的代码应该是这样的:
var simulationResults = new ConcurrentBag<double>();
Parallel.For(0, 10000, (i, loopState) =>
{
//I assume this method is thread safe
progressAction?.Invoke();
double aResult = someMethod()
if (double.IsNaN(aResult))
{
//or loopState.Break();
loopState.Stop();
}
simulationResults.Add(aResult);
});
使用 Parallel.For
而不是 PLINQ
的一个缺点是当您需要保留输入数据顺序时。
使用 PLINQ
您可以使用 AsOrdered
方法轻松完成,而使用 Parallel.For
则要困难得多(但仍有可能,请参阅链接文章以获取详细信息)。
但是你没有提到这个要求,所以我在提供的代码中忽略了它。
如果你想在第一个不是数字的地方换行,使用TakeWhile
。如果你只是想跳过它们,你可以使用 Where
.
var simulationResults =
Enumerable.Range(0, 10000)
.AsParallel()
.WithCancellation(token ?? CancellationToken.None)
.Select(z =>
{
progressAction?.Invoke();
return someMethod();
})
.TakeWhile(result => !double.IsNaN(result));
有什么方法可以从 PLINQ 的 SELECT 块中突破吗?基于 SELECT 块中的条件,我想打破循环。到目前为止,我看到了各种使用取消令牌从循环外部中断 PLINQ 的示例。但是,我的问题是从循环内部中断。
这是我的代码:
var simulationResults =
Enumerable.Range(0, 10000)
.AsParallel()
.WithCancellation(token ?? CancellationToken.None)
.Select(z =>
{
progressAction?.Invoke();
double aResult = someMethod()
if (double.IsNaN(aResult))
{
// todo: Find a way to Break the Parallel loop.
}
return aResult;
});
如果没有可用的技术,也许我需要找到一种困难的方法,比如使用带有 volatile 关键字的布尔标志。我将在 PLINQ Select 块中使用 lock 语句设置该标志,然后下一次,它将读取该标志并确定它是否应该中断。但是,不确定这是否是一个好的解决方案。此外,我非常不愿意在 PLINQ 内部使用锁,这违背了并行计算的目的。使用 Lock,我可能会强制执行同步而无法获得并行编程的好处。
那么有更好的主意吗?
我建议你使用 Parallel.For
而不是 PLINQ
:
Why not PLINQ?
Even though PLINQ provides support for exiting from within a query execution, the differences between PLINQ’s exit mechanism and Parallel.ForEach are substantial. To exit from a PLINQ query, supply the query with a cancellation token, as explained here. With Parallel.ForEach, exiting flags are polled on every iteration. In PLINQ, the cancellation token is polled once every so often, so you cannot assume that a cancelled query will stop quickly.
取自退出操作部分When To Use Parallel.ForEach and When to Use PLINQ。
在上面的文章中,您可以找到有关如何使用 ParallelLoopState
的 Stop
或 Break
方法停止 Parallel.ForEach
/Parallel.For
的详细说明,这些方法是那里有完整的描述 here:
In this context, "break" means complete all iterations on all threads that are prior to the current iteration on the current thread, and then exit the loop. "Stop" means to stop all iterations as soon as convenient.
使用 Parallel.For
你的代码应该是这样的:
var simulationResults = new ConcurrentBag<double>();
Parallel.For(0, 10000, (i, loopState) =>
{
//I assume this method is thread safe
progressAction?.Invoke();
double aResult = someMethod()
if (double.IsNaN(aResult))
{
//or loopState.Break();
loopState.Stop();
}
simulationResults.Add(aResult);
});
使用 Parallel.For
而不是 PLINQ
的一个缺点是当您需要保留输入数据顺序时。
使用 PLINQ
您可以使用 AsOrdered
方法轻松完成,而使用 Parallel.For
则要困难得多(但仍有可能,请参阅链接文章以获取详细信息)。
但是你没有提到这个要求,所以我在提供的代码中忽略了它。
如果你想在第一个不是数字的地方换行,使用TakeWhile
。如果你只是想跳过它们,你可以使用 Where
.
var simulationResults =
Enumerable.Range(0, 10000)
.AsParallel()
.WithCancellation(token ?? CancellationToken.None)
.Select(z =>
{
progressAction?.Invoke();
return someMethod();
})
.TakeWhile(result => !double.IsNaN(result));