我有一个以简单对象(记录)形式生成数据的线程。该线程可以为每个成功通过过滤器并实际排队的记录生成一千条记录。一旦对象入队,它就是只读的。
我有一个锁,一旦记录通过了过滤器,我就获得了这个锁,然后我将该项添加到producer_queue的后面。
在消费者线程上,我获取了锁,确认producer_queue不为空, 将consumer_queue设置为等于producer_queue,创建一个新的(空)队列,并将其设置在producer_queue上。没有任何进一步的锁定,我处理consumer_queue直到它为空并重复。
在大多数机器上一切都运行得很漂亮,但在一个特定的双四核服务器上,我在~1 / 500k迭代中看到一个对象,当我从consumer_queue中读出它时,它没有完全初始化。条件是如此短暂,以至于当我在检测到条件后转移对象时,字段在90%的时间内都是正确的。
所以我的问题是这样的:我怎样才能确保在交换队列时对对象的写入被刷新到主内存?
编辑:
在生产者线程上: (上面的producer_queue是m_fillingQueue;上面的consumer_queue是m_drainingQueue)
private void FillRecordQueue() {
while (!m_done) {
int count;
lock (m_swapLock) {
count = m_fillingQueue.Count;
}
if (count > 5000) {
Thread.Sleep(60);
} else {
DataRecord rec = GetNextRecord();
if (rec == null) break;
lock (m_swapLock) {
m_fillingQueue.AddLast(rec);
}
}
}
}
在消费者主题中:
private DataRecord Next(bool remove) {
bool drained = false;
while (!drained) {
if (m_drainingQueue.Count > 0) {
DataRecord rec = m_drainingQueue.First.Value;
if (remove) m_drainingQueue.RemoveFirst();
if (rec.Time < FIRST_VALID_TIME) {
throw new InvalidOperationException("Detected invalid timestamp in Next(): " + rec.Time + " from record " + rec);
}
return rec;
} else {
lock (m_swapLock) {
m_drainingQueue = m_fillingQueue;
m_fillingQueue = new LinkedList<DataRecord>();
if (m_drainingQueue.Count == 0) drained = true;
}
}
}
return null;
}
消费者受价格限制,因此无法超越消费者。
我看到的行为有时候Time字段的读取时间为DateTime.MinValue;当我构造字符串以抛出异常时,它完全没问题。
答案 0 :(得分:2)
您是否尝试过明显的问题:是否在精美的8核盒上应用了微代码更新(通过BIOS更新)?您是否运行Windows更新以获取最新的处理器驱动程序?
乍一看,看起来你正在锁定你的容器。所以我推荐系统方法,因为听起来你没有在一个好的双核盒上看到这个问题。
答案 1 :(得分:0)
假设这些实际上是与m_fillingQueue
变量交互的唯一方法,并且在DataRecord
创建它之后GetNextRecord()
无法更改(希望只读属性?),那么至少在它面前的代码似乎是正确的。
在这种情况下,我建议GregC的答案是首先要检查的;确保故障机器已完全更新(OS / drivers / .NET Framework),因为lock
语句应包含所有必需的内存屏障,以确保rec
变量完全从任何缓存中清除在将对象添加到列表之前。