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关于锁的文章,并且仍然很了解如何在更复杂的情况下使用锁。
还需要在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函数从队列中获取项目。当它不能得到一个项目时,它休眠,当它能得到一个项目时,它处理它。
将更大的行组放在队列中可能会产生更好的性能,因为您的线程不会花费大量时间尝试获取锁。