我有些困惑,不知道何时变量会被强制写入内存,即使在互斥块之外。很抱歉下面的代码逻辑很复杂,因为我剥离了涉及
我有以下设置,我“相当”确定没有竞态条件:
被标记为星号
一种“保证”的解决方案是在
谢谢!
reader
决定某些数据是否过期的逻辑。需要注意的重要事项是,99.9%的情况下,读者将采用fast path
,同步必须非常快,这就是为什么我使用原子int32
来传递陈旧性和慢路径是否现在必要的原因。我有以下设置,我“相当”确定没有竞态条件:
#define NUM_READERS 10
BigObject mSharedObject;
std::atomic_int32_t mStamp = 1;
std::mutex mMutex;
std::condition_variable mCondition;
int32_t mWaitingReaders = 0;
void reader() {
for (;;) { // thread loop
for (;;) { // spin until stamp is acceptible
int32_t stamp = mStamp.load();
if (stamp > 0) { // fast path
if (stampIsAcceptible(stamp) &&
mStamp.compare_exchange_weak(stamp, stamp + 1)) {
break;
}
} else { // slow path
// tell the loader (writer) that we're halted
std::unique_lock<mutex> lk(mMutex);
mWaitingReaders++;
mCondition.notify_all();
while (mWaitingReaders != 0) {
mCondition.wait(lk);
} // ###
lk.unlock();
// *** THIS IS WHERE loader's CHANGES TO mSharedObject
// *** MUST BE VISIBLE TO THIS THREAD!
}
}
// stamp acceptible; mSharedObject guaranteed not written to
mSharedObject.accessAndDoFunStuff();
mStamp.fetch_sub(1); // part of hidden staleness logic
}
}
void loader() {
for (;;) { // thread loop
// spin until we somehow decide we want to change mSharedObject!
while (meIsHappySleeping()) {}
// we want to modify mSharedObject, so set mStamp to 0 and wait
// for readers to see this and report that they are now waiting
int32_t oldStamp = mStamp.exchange(0);
unique_lock<mutex> lk(mMutex);
while (mWaitingReaders != NUM_READERS) {
mCondition.wait(lk);
}
// all readers are waiting. start writing to mSharedObject
mSharedObject.loadFromFile("example.foo");
mStamp.store(oldStamp);
mWaitingReaders = 0; // report completion
lk.unlock();
mCondition.notify_all();
// *** NOW loader's CHANGES TO mSharedObject
// *** MUST BE VISIBLE TO THE READER THREADS!
}
}
void setup() {
for (int i = 0; i < NUM_READERS; i++) {
std::thread t(reader); t.detach();
}
std::thead t(loader); t.detach();
}
被标记为星号
***
的部分是我关注的焦点。这是因为尽管我的代码排除了竞态条件(据我所知),但在被loader()
写入时,mSharedObject
仅由互斥锁保护。由于reader()
需要非常快速(如上所述),我不希望对mSharedObject
进行只读访问时需要受到互斥锁的保护。一种“保证”的解决方案是在
###
行引入一个线程局部变量const BigObject *latestObject
,将其设置为&mSharedObject
,然后用它进行访问。但这是否是不好的做法?而且真的有必要吗?原子操作/互斥锁释放操作能够保证读者看到更改吗?谢谢!
***
段之后,它会回到循环,连续访问mStamp
,然后调用mSharedObject.accessAndDoFunStuff()
。但是mStamp
是由loader()
在loadFromFile()
之后进行 seq-con 写入的。这感觉应该足以强制reader()
看到mSharedObject
的变化。我对吗?谢谢! - bombax