我需要一种非常快速(在“读者成本低”而不是“延迟低”的意义上)的线程之间的变更通知机制,以便更新读取缓存:
情况
线程W
(写入器)只偶尔更新数据结构(S
)(在我的案例中是映射中的设置)。
线程R
(读取器)维护S
的缓存,并经常读取此缓存。当线程W
更新S
时,线程R
需要在合理的时间内(10-100ms)被通知更新。
架构是ARM,x86和x86_64。我需要支持使用gcc 4.6及更高版本的C++03
。
代码
类似于以下内容:
// variables shared between threads
bool updateAvailable;
SomeMutex dataMutex;
std::string myData;
// variables used only in Thread R
std::string myDataCache;
// Thread W
SomeMutex.Lock();
myData = "newData";
updateAvailable = true;
SomeMutex.Unlock();
// Thread R
if(updateAvailable)
{
SomeMutex.Lock();
myDataCache = myData;
updateAvailable = false;
SomeMutex.Unlock();
}
doSomethingWith(myDataCache);
我的问题
在线程R
中,"快速路径"(没有可用的更新)中不会出现锁定或屏障。这是一个错误吗?这种设计的后果是什么?
我需要将updateAvailable
标记为volatile
吗?
R
最终会获得更新吗?
我目前的理解
数据一致性方面是否安全?
这看起来有点像"双重检查锁定"。根据http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html,可以使用内存屏障在C++中修复它。
然而,这里的主要区别在于,在读者快速路径中从未触及/读取共享资源。当更新缓存时,互斥锁保证了一致性。
R
是否会获得更新?
这就是棘手的地方。据我所知,运行线程R
的CPU可能会无限期地缓存updateAvailable
,有效地将读取移动到实际的if
语句之前。
因此,更新可能需要等到下一次缓存刷新,例如当另一个线程或进程被调度时。
sig_atomic_t
不是原子变量,因此仍然没有原子操作,因此行为未定义。在C++03中使用https://gcc.gnu.org/onlinedocs/gcc-4.6.4/gcc/Atomic-Builtins.html或(自GCC 4.7以来)https://gcc.gnu.org/onlinedocs/gcc-4.7.4/gcc/_005f_005fatomic-Builtins.html进行原子操作。 - Jonathan Wakelyj=0;
优化为j=a; j-=a;
,我曾经见过一些同样令人惊讶并导致代码出错的真实世界优化案例。 - David Schwartz