18

假设我有一个指向整数的指针。

volatile int* commonPointer = new int();

而且我有多个线程取消引用该指针。

int blah = *commonPointer;

但是,一个线程需要更改该指针的地址:

int* temp = new int();
int* old = commonPointer;
InterlockedExchange(&commonPointer,temp);
delete old;

现在,让我们忽略一些线程可能正在读取“旧”值,而有些线程可能正在读取“新”值的事实,这对我来说不是问题。

会不会有一个线程开始取消引用指针的情况,就像地址被删除一样,然后得到一个异常?
还是取消引用原子性足够,以至于不会发生?

4

4 回答 4

22

在这种情况下,C++ 标准中没有任何内容可以保证原子性。

您必须使用互斥锁保护相关代码区域:即使std::atomic是不够的,因为它只会提供对指针的原子访问,但不包括取消引用操作。

于 2013-05-20T07:29:30.377 回答
5

也许,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 的无锁实现已经足够复杂,因此可能会在库实现中使用锁或自旋锁(即使该实现在您的平台上可用)。

于 2013-05-20T09:20:27.297 回答
3

首先,volatile声明中的 the 没有任何实际效果。其次,一旦您在一个线程中修改了一个值,并在多个线程中访问它,所有访问都必须受到保护。否则,您有未定义的行为。我不知道保证 InterlockedExchange给出了什么,但我确信它对任何不调用它的线程都没有影响。

于 2013-05-20T08:12:53.807 回答
1

Edit2:对不起,不,这无济于事。您需要围绕访问的互斥锁 - 编译器生成的代码有可能(很可能)将指针加载到寄存器[或其他存储,如堆栈,如果它是没有寄存器的处理器],然后访问指针指向的内存同时,指针正在被另一个线程更新。保证指针正确的唯一方法是使用互斥锁或类似结构来关闭整个访问块。其他任何事情都有可能失败。

正如 syam 所说,该标准不能保证即使读取 hte 指针指向的 32 位值也是原子的——它取决于系统的实现。但是,如果您问“我会得到一个旧值还是新值”,那么至少 x86 和 x86-64 将保证这一点。其他机器架构可能不会(SMP 68000 处理器上的 32 位 int 实现不能保证,因为一次写入是 16 位,第二个处理器可能已经写入了一半,但不是另一个 - 不是我知道一个有 68000 个处理器的 SMP 系统正在构建中)。

(这InterlockedExchange不是“标准”函数)将保证该线程的处理器具有对指针本身的独占访问权限,因此这样做是安全的——此时没有其他处理器能够访问指针。这就是 x86 架构中“锁定”指令的全部意义——它们是“安全的”(而且相当慢,但假设您不是每次都这样做……)。

编辑:请注意,您必须小心commonPointer自己,因为编译器可能没有意识到您正在使用另一个线程来更新它。因此,您可能仍在读取 OLD 指针值。

调用一个函数[没有被内联到虚无]或声明指针volatile int * volatile commonPointer;应该可以解决问题。[提示人们对我的使用答案投反对票volatile,因为“解决方案没有问题,volatile正如之前有人发布的那样]。

[见上面的编辑2]

于 2013-05-20T07:45:19.567 回答