GNU C ++中的原子交换

我想验证我的理解是否正确。这种事情很棘手,所以我几乎可以肯定我错过了什么。我有一个由实时线程和非实时线程组成的程序。我希望非RT线程能够将指针交换到RT线程使用的内存。 从文档中,我的理解是,这可以通过以下方式完成:
// global
Data *rt_data;

Data *swap_data(Data *new_data)
{
#ifdef __GNUC__
    // Atomic pointer swap.
    Data *old_d = __sync_lock_test_and_set(&rt_data, new_data);
#else
    // Non-atomic, cross your fingers.                                          
    Data *old_d = rt_data;
    rt_data = new_data;
#endif
    return old_d;
}
这是程序中唯一的位置(初始设置除外),其中
rt_data
被修改。当在实时上下文中使用
rt_data
时,它将被复制到本地指针。对于
old_d
,稍后当确定未使用旧内存时,它将在非RT线程中释放。它是否正确?我需要
volatile
吗?我应该调用其他同步原语吗? 顺便说一句,我在C ++中这样做,虽然我对C的答案是否有所不满感兴趣。 提前谢谢。     
已邀请:
C/C++
中编写并发代码时,一般不要使用
volatile
volatile
的语义是如此接近你想要的它是诱人的但最终volatile不够。不幸的是
Java/C# volatile != C/C++ volatile
。 Herb Sutter有一篇很棒的文章解释了令人困惑的混乱。 你真正想要的是记忆围栏。
__sync_lock_test_and_set
为您提供围栏。 将rt_data指针复制(加载)到本地副本时,还需要一个内存栏。 无锁编程很棘手。如果您愿意使用Gcc的c ++ 0x扩展,那么它会更容易:
#include <cstdatomic>

std::atomic<Data*> rt_data;

Data* swap_data( Data* new_data )
{
   Data* old_data = rt_data.exchange(new_data);
   assert( old_data != new_data );
   return old_data;
}

void use_data( )
{
   Data* local = rt_data.load();
   /* ... */
}
    
更新:这个答案是不正确的,因为我错过了
volatile
保证对
volatile
变量的访问不重新排序的事实,但是对于其他非
volatile
访问和操作没有提供这样的保证。内存栅栏确实提供了这样的保证,并且对于该应用程序是必需的。我的原始答案如下,但不要采取行动。请参阅此答案,以便在我的理解中找到一个很好的解释,导致以下错误的响应。 原始答案: 是的,你的
rt_data
声明需要
volatile
;任何时候可以在访问它的线程的控制流之外修改变量,它应该被声明为
volatile
。虽然你可以在没有
volatile
的情况下离开,因为你要复制到本地指针,
volatile
至少有助于文档,并且还会抑制一些可能导致问题的编译器优化。考虑以下从DDJ采用的示例:
volatile int a;
int b;
a = 1;
b = a;
如果
a
的值可能在
a=1
b=a
之间变化,那么
a
应该声明为
volatile
(当然,除非将过期值分配给
b
是可接受的)。多线程,尤其是原子基元,构成了这种情况。这种情况也是由信号处理程序和映射到奇数存储器位置的变量(例如硬件I / O寄存器)修改的变量触发的。另见这个问题。 否则,它看起来很好。 在C中,我可能会使用GLib提供的原子基元。如果原子操作不可用,它们将在可用的情况下使用原子操作并回退到基于互斥体的缓慢但正确的实现。 Boost可能为C ++提供类似的东西。     

要回复问题请先登录注册