mutexブロックの外側でも変数を強制的にメモリに書き込む際には、何か問題があります。 reader
のデータが失効しているかどうかを判断するロジックを取り除いたので、以下の複雑なコードについてはお詫び申し上げます。注目すべき重要なことは、時間の99.9%で、読者はfast path
をとり、同期は非常に高速でなければならないということです。なぜなら、アイドルint32
を使用して、失速と低速パスが現在必要かどうかを通信するのです。C++の並行性:mutex以外での変数の可視性
#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();
}
星***
でマーク部分が私に関係するものです。
が、私は私は次のセットアップを持っている「かなり」特定のレースフリーです。私のコードではレースを除外していますが(私が見る限り)、mSharedObject
はloader()
によって書き込まれている間だけミューテックスによって保護されているためです。 reader()
は(上記のように)非常に高速である必要があるため、私はその読み取り専用アクセスをmSharedObject
にミューテックスで保護する必要はありません。
「const BigObject *latestObject
」の###
のスレッドローカル変数を導入することです。これは&mSharedObject
に設定され、アクセスに使用されます。しかし、この悪い習慣は?本当に必要ですか?アトミック操作/ mutexのリリース操作によって、読者は変更を確認できますか?
ありがとうございます!
はい、私はこれについての研究に時間を費やしていませんでした...私はそれについて少しのメモをしました:http://imgur.com/a/Dpx46 今私の原子操作はすべてですデフォルトの順次整合性。あなたの答えには違いがありますか? – bombax
私はちょうど何か気づいた。読者の '*** 'セグメントの後、ループバックし、' mSharedObject.accessAndDoFunStuff() 'を呼び出す前に' mStamp'に逐次一貫してアクセスします。しかし、 'mStamp'は' loadFromFile() 'の直後に' loader() 'によって書かれたseq-conです。これは 'reader()'に 'mSharedObject'への変更を見せてもらうのに十分であるように感じます。私は正しい?ありがとう!! – bombax