c#锁不允许关键代码正确运行

本文关键字:运行 代码 不允许 | 更新日期: 2023-09-27 18:06:57

我启动了一个从数据库读取数据的新线程。每个X记录都有一个标志,主线程将处理检索到的记录,保留一些记录,丢弃其他记录。当标志发出信号时,我使用锁来允许读取线程等待,直到处理器线程完成对记录的处理。然而,锁似乎没有这样做,因为当我遍历记录时,更多的记录不断被添加(表明读取线程仍在读取)。这会导致集合被修改,从而导致invalidoperationexception。

也许我误解了"lock"的目的,或者我没有正确地使用它。这是我所拥有的pseudo:

readonly object locker = new object();
Dictionary screened = new Dictionary;
Search(){
    Thread reader = new Thread( () => Read("search terms") );
    reader.Start();
    while( found < desiredAmount ){
        if(SIGNAL){
            lock(locker){
                ProcessRecords();
            }
        }
     }
}
Read(){
    Connect to DB
    while(reader.Read()){
        screened.add(record);
    }
}
ProcessRecords(){
    foreach(var x in screened){
        //process record 
    }
}

我希望pseudo足够好,从我的理解Read()不应该在锁块中执行。请帮助我更好地理解lock。

PS是的,我已经阅读了msdn关于锁的文章,并且仍然很了解如何在更复杂的情况下使用锁。

c#锁不允许关键代码正确运行

还需要在while循环周围加一个锁。如果有两个或多个线程争用同一个锁,这个锁就可以工作,在上面的示例中,没有争用,因为除了第一个线程之外,没有其他线程请求锁。

Read(){
    Connect to DB
        while(reader.Read()){
            lock(locker)
                screened.add(record);
        }
}

更好的方法是将锁也放在ProcessRecords()中

screened.Add()不受保护,AFAICT

尝试像这样添加锁:

while(reader.Read()){
    lock(locker)
    {
        screened.add(record);
    }
}

我不确定我理解你的代码…但是如果你使用字典来存储和读取数据,那么我强烈建议使用ConcurrentDictionary -它是线程安全的,并且非常快(因为大多数操作都是无锁实现的)…

信息见:

  • http://www.dotnetperls.com/concurrentdictionary
  • http://geekswithblogs.net/BlackRabbitCoder/archive/2011/02/17/c.net-little-wonders-the-concurrentdictionary.aspx

如果您希望两个线程互斥,则需要在两个线程中锁定同一个对象。

因此,在较高级别上,您希望重复以下步骤,直到处理完所有记录:

  • 获取reader线程中的锁
  • 读取一些记录
  • 在主线程中获取锁
  • 处理记录

在使用一个集合时,如果在其中保留了任何接受的条目,则处理将重复地重新处理它们。

一个更好的方法是生产者/消费者队列,可能使用框架4.0的BlockingCollection<T>

生产者/消费者队列的一般思想是,数据库读取器将项目放入队列,处理程序将它们从队列中删除,如果保留,则将它们放在另一个集合中。

通常,锁用于保护数据不被多个线程同时访问。

需要访问数据的代码部分必须首先获得锁。

一个更好的方法是在线程之间传递消息。

假设你的两个线程(reader和search)都访问同一个system . collections . concurrent . concurrentqueue。一旦阅读器有了所需的行数,它就会在队列中放置一个对象集合或其他东西。

你的搜索线程尝试使用TryDeque函数从队列中获取项目。当它不能得到一个项目时,它休眠,当它能得到一个项目时,它处理它。

将更大的行组放在队列中可能会产生更好的性能,因为您的线程不会花费大量时间尝试获取锁。