如何在条件语句

时间:2016-06-24 20:01:42

标签: c# multithreading linq linq-to-sql deferred-execution

我有一个线程定期检查我的一个MS SQL表中是否有“Processed”位字段设置为0的任何记录。然后线程使用这些记录执行一些代码,然后将其Processed位设置为1;基本上将它用作队列。我用来检索这些记录的Linq查询跨越多行并且非常复杂(原因不明白),所以这是一个非常简化的版本:

var RecordsToProcess = MyTable.Where(i => i.Processed == 0); // Very simplified

我需要等到所有记录都处理完后再继续,所以我想使用这样的东西:

while (RecordsToProcess.Count() > 0)
{
    System.Threading.Thread.Sleep(1000);
}

问题在于,虽然线程确实处理了记录并将其Processed位设置为1,但条件语句中的RecordsToProcess.Count()值永远不会减少,因此我们得到一个无限循环。我的猜测是调用.Count()将整数存储在内存中,然后循环的每次迭代都会查看该值,而不是查询数据库以获取当前计数。我可以通过将查询移动到条件语句中来获得我想要的行为,如下所示:

while (true)
{
    if (MyTable.Where(i => i.Processed == 0).Count() > 0)
        System.Threading.Thread.Sleep(1000);
    else
        break;
}

由于我实际使用的查询比本例中的查询复杂得多,因此这样做会使其难以阅读。有什么我可以使用的类似于RecordsToProcess.Count()> 0,但是每次迭代都会查询该数据库而不是使用存储在内存中的初始计数(假设我是正确的)?

注意:我通常不会像这样使用潜在危险的while循环,但我只需要运行此页面最多4或5次,然后再也不会。所以我不太担心它。

2 个答案:

答案 0 :(得分:4)

根据评论修改原始帖子。

我认为问题的一部分是编译器如何优化循环。

您的查询中可能存在缓存数据的内容。如果整个查询使用延迟评估,除了在循环中检查Count之外,每次在查询上调用Count时,都会重新评估它。在第二个示例中,整个查询都在循环中,因此每次都必须重新评估,无论是否实际使用延迟评估。我会检查MSDN文档中关于您正在使用的运算符的remarks

我还建议在这种情况下使用Any而不是Count来提高性能和清晰度。根据您正在迭代的内容,Count通常会遍历集合以查看有多少元素,但Any更加懒惰。在LINQ to Object中,Count()针对实现ICollection的序列进行了优化,使用Count属性,这比迭代快得多,Any()在找到1后停止检查元件。正如下面的Erik所建议的那样,在LINQ to SQL中,可能会在TOP 1语句中添加SELECT之类的内容。我认为SQL有自己的COUNT优化,但我还没有做过任何研究。

在适当的情况下使用Any()也可以通过删除Count() > 0中的运算符来提高可读性,并且更清楚地表达您对bool而非int感兴趣1}}。

我会像你这样实现你的方法:

var query = MyTable.Where(i => i.Processed == 0);
while(true) {
    if (!query.Any()) break;
    Thread.Sleep(1000);
}

或更好的是,如果你能让它懒惰地执行:

var query = MyTable.Where(i => i.Processed == 0);
while(query.Any()) { Thread.Sleep(1000); }

但是,正如其他答案所述,有关如何构建查询的更多信息会有所帮助。

答案 1 :(得分:0)

您没有刷新每个循环上的RecordsToProcess变量

While(RecordsToProcess.Count() > 0)
{
  System.Threading.Thread.Sleep(1000);
  RecordsToProcess = MyTable.Where(i => i.Processed == 0);
}