在LINQ查询中突破Parallel.ForEach

时间:2011-11-29 12:09:18

标签: .net linq-to-sql task-parallel-library

我正在使用Parallel.ForEach循环LINQ to SQL映射。一旦处理了一定数量的元素,我就会脱离循环。处理停止,但是在出现错误之前,循环会挂起几秒钟:

System.Data.SqlClient.SqlErrorCollection: Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.

如何让循环正常退出?通过在查询中附加ToList()来强制本地集合是不可取的。我也尝试在using块中包装所有内容但无济于事。注意,那个

以下是代码:

var query = SomeDataContext.SomeTableMapping; // All ok, if I append Take(maxRecords)
int maxRecords = 1000;
Parallel.ForEach(query, (queryResult, pLoopState, idx) =>
{
    // Do whatever here on queryResult.
    if (idx > maxRecords)
    {
         Console.WriteLine("Reached maximum number of records: {0}", maxRecords);
         pLoopState.Break();
    }
});

谢谢,

/大卫

3 个答案:

答案 0 :(得分:5)

您可以使用CancellationTokenSource类实例来取消并行循环。更多信息:How to: Cancel a Parallel.For or ForEach Loop

这里要注意的重要一点是,当在并行循环内部调用取消令牌时,不会停止已经运行的迭代的执行。它只是不会启动任何新的迭代。

使用ParallelLoopState类的Break()和Stop()方法还有其他方法可以打破/停止并行循环。初始化Parallel.For / ForEach循环时,您可以传递ParallelLoopState类的实例并使用该实例来调用Break / Stop方法。

Break方法与Stop略有不同。在Stop的情况下,框架请求尽快停止迭代。使用Break,框架请求循环尽快停止执行当前迭代之外的迭代。如果您正在寻找特定的键/文本,并且想要在找到后突破,那么您应该使用Stop()方法。

答案 1 :(得分:0)

即使状态Break是停止并行循环的两种方法之一,但是断开委托对循环执行没有任何影响,因为委托的主体在每次执行时都与循环结构断开连接。这就解释了为什么你会观察它暂停一段时间(而且我同意,在第一眼看来,这与Break在循环结构中习惯的语义不同似乎是违反直觉的)。循环仍然在运行!

解决方案将取决于您在循环中尝试完成的内容,但您可能希望尝试...

if (pLoopState.ShouldExitCurrentIteration)
{
     return;
}

而不是休息。这应该会让你超过它似乎挂起的部分。

答案 2 :(得分:0)

您应该检查正在执行的SQL语句而不是循环。循环将并行处理语句返回的所有行。打破循环不会停止语句的执行,它只会停止处理结果。

LINQ在您尝试枚举其结果时执行查询,而不是在您尝试访问其中一个时执行查询。在您的示例中,这是您将查询变量传递给Parallel.ForEach并将其转换为IEnumerable的那一刻。然后Parallel.ForEach获取每个结果行并尝试并行处理它。

我怀疑你有一个没有任何WHERE标准的大表。因此,一旦默认执行超时过去(我认为大约60秒),您的连接就会超时。如果要检索特定数量的行,则应使用SQL中的e TOP语句或LINQ中的Take()方法,例如:通过调用

Parallel.ForEach(query.Take(10),

而不是简单地传递查询

如果要限制SQL或LINQ语句返回的行,则应在语句本身中执行此操作,而不是在返回结果后尝试限制结果。检索不必要的行结果会显着降低数据库服务器的速度并导致可能的死锁。