2011-11-29 85 views
4

我正在使用Parallel.ForEach循环LINQ to SQL映射。一旦处理了一定数量的元素,我就会跳出循环。处理停止,但随后的循环挂起几秒钟之前,我得到一个错误:通过LINQ查询打破Parallel.ForEach

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(); 
    } 
}); 

感谢,

/大卫

回答

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()方法。

0

尽管状态Break是停止并行循环的两种方式之一,但由于代理的主体在每次执行时都从循环结构断开连接,所以断开委托对循环执行没有任何影响。这就解释了为什么你观察它挂了一段时间(我会同意,第一次看到它似乎与直觉相反,Break与我们在循环构造中习惯的语义不同)。循环仍在运行!

该解决方案将取决于你想在循环中实现什么,但你可能要进行实验,...

if (pLoopState.ShouldExitCurrentIteration) 
{ 
    return; 
} 

,而不是中断。这应该会让你超越它似乎挂起的部分。

+0

调用中断,不会停止执行小于当前正在运行的迭代索引的迭代。假如在索引号20上调用Break,那么直到1-19的所有迭代都完成后,循环才会停止。是的,我同意检查“ShouldExitCurrentIteration”肯定有助于提高响应速度。如果您想在遇到某些情况时立即分手,则Stop()是您最好的选择。 –

+0

同意。感谢Pawan! –

+0

探测'pLoopState.ShouldExitCurrentIteration'没有帮助,错误仍然存​​在。请注意,我编辑了我的原始帖子。我实际上遍历映射本身,即使错误是相同的: - / –

0

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

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

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

Parallel.ForEach(query.Take(10), 

而不是简单地传递查询

如果您想限制SQL或LINQ语句返回的行,您应该在语句本身中执行该操作,而不是试图在返回后限制结果。检索不必要的行结果会显着减慢数据库服务器并导致可能的死锁。