假设我有一个指向整数的指针。
volatile int* commonPointer = new int();
我有多个线程可以取消引用该指针。
int blah = *commonPointer;
但是,一个线程需要更改指针的地址:
int* temp = new int();
int* old = commonPointer;
InterlockedExchange(&commonPointer,temp);
delete old;
现在,让我们忽略一些线程可能正在读取“旧”值的事实,有些线程可能正在读取“新”值,这在我的情况下不是问题。
是否存在一个线程开始取消引用指针的情况,就像删除地址一样,然后获得异常?
或者解除引用原子是否会发生?
答案 0 :(得分:22)
在这种情况下,C ++标准中没有任何内容可以保证原子性。
您必须使用互斥锁保护相关代码区域:偶数std::atomic
是不够的,因为它只提供对指针的原子访问,但不包括取消引用操作。
答案 1 :(得分:4)
也许,C ++ 11
atomic<shared_ptr<int> >
满足您的需求。它会阻止旧值消失,直到至少有一个对该值的引用有效。
atomic<shared_ptr<int> > commonPointer;
// producer:
{
shared_ptr<int> temp(new int);
shared_ptr<int> old= atomic_exchange(&commonPointer, temp);
//...
};// destructor of "old" decrements reference counter for the old value
// reader:
{
shared_ptr<int> current= atomic_load(&commonPointer);
// the referent pointed by current will not be deleted
// until current is alive (not destructed);
}
但是,原子共享ptr的无锁实现非常复杂 可能会在库实现中使用锁或自旋锁(即使您的平台上有可用的实现)。
答案 2 :(得分:3)
首先,声明中的volatile
没有任何真实的
影响。第二,只要你修改一个值
线程,并在多个线程中访问它,所有访问
必须得到保护。否则,您有未定义的行为。我不知道有什么保证
InterlockedExchange
给出了,但我确信它没有任何影响
在任何不调用它的线程上。
答案 3 :(得分:1)
Edit2:抱歉,不,它无济于事。你需要在访问周围使用互斥锁 - 编译器生成的代码很可能(非常可能)将指针加载到寄存器[或其他存储器,例如堆栈,如果它是没有寄存器的处理器],然后访问存储器指针指向at,同时,指针正被另一个线程更新。保证指针正确的唯一方法是使用互斥锁或类似结构来关闭整个访问块。其他任何事情都可能失败。
正如syam所说,标准并不保证即使读取指针指向的32位值也是原子的 - 它依赖于系统的实现。但是,如果你问“我会得到一个值是旧值还是新值”,那么至少x86和x86-64将保证这一点。其他机器架构可能没有(SMP 68000处理器上的32位int实现不能保证它,因为一次写入是16位,而第二个处理器可能写了一半,但不是另一个 - 不是我知道有一个68MP处理器的SMP系统正在构建中。
InterlockedExchange
(不是“标准”函数)将保证该线程的处理器具有对指针本身的EXCLUSIVE访问权限,因此可以安全地执行 - 没有其他处理器能够访问指针在那时候。这是x86架构中“锁定”指令的重点 - 它们是“安全的”(而且相当慢,但假设你不是每次都这样做......)。
编辑:请注意,您必须小心commonPointer
本身,因为编译器可能没有意识到您正在使用另一个线程来更新它。所以你可能仍在读取OLD指针值。
调用函数[没有内联到虚无]或声明指针volatile int * volatile commonPointer;
应该可以解决问题。 [提示人们使用volatile
来回答我的回答,因为“解决方案没有问题volatile
,就像之前发布的人一样。”
[见上面的edit2]