edA*_*a-y 12 c++ multithreading memory-model memory-barriers c++11
在的C++ 0x草案有围栏的概念似乎很明显,从围墙的CPU /芯片级的概念,或者说什么linux内核的家伙想到的围栏.问题在于草案是否真的意味着一个极其受限制的模型,或者措辞是否很差,它实际上意味着真正的围栏.
例如,在29.8 Fences下它表示如下:
如果存在原子操作X和Y,则释放围栏A与获取围栏B同步,两者都在某个原子对象M上操作,使得A在X之前被排序,X修改M,Y在B之前被排序,并且Y读取该值如果是释放操作,则由X写入或由假设释放序列中的任何一方写入的值X将结束.
它使用这些术语atomic operations和atomic object.草案中定义了这样的原子操作和方法,但它仅仅意味着那些吗?一个释放栅栏听起来像一个店围栏.在围栏之前不保证写入所有数据的商店围栏几乎是无用的.类似于装载(获取)围栏和完整围栏.
那么,C++ 0x中的栅栏/栅栏是否适当的栅栏和措辞是否非常差,或者它们是否如所描述的那样极其受限制/无用?
就C++而言,假设我有这个现有的代码(假设现在可以使用围栏作为高级构造 - 而不是在GCC中使用__sync_synchronize):
Thread A:
b = 9;
store_fence();
a = 5;
Thread B:
if( a == 5 )
{
load_fence();
c = b;
}
Run Code Online (Sandbox Code Playgroud)
假设a,b,c的大小在平台上具有原子拷贝.以上意味着c只会被分配9.注意我们并不关心线程B何时看到a==5,只是当它看到它时b==9.
C++ 0x中保证相同关系的代码是什么?
答案:如果您阅读我选择的答案和所有评论,您将获得情况的要点.C++ 0x似乎强制您使用带栅栏的原子,而普通硬件栅栏没有此要求.在许多情况下,只要sizeof(atomic<T>) == sizeof(T)和,这仍然可以用来代替并发算法atomic<T>.is_lock_free() == true.
不幸的是,这is_lock_free不是一个constexpr.这将允许它用于static_assert.有atomic<T>简并使用锁通常是一个坏主意:使用互斥体将具有比互斥设计的算法可怕的争夺问题原子的算法.
Ant*_*ams 15
栅栏提供订货上的所有数据.但是,为了保证一个线程的fence操作对于第二个可见,您需要对该标志使用原子操作,否则您将进行数据竞争.
std::atomic<bool> ready(false);
int data=0;
void thread_1()
{
data=42;
std::atomic_thread_fence(std::memory_order_release);
ready.store(true,std::memory_order_relaxed);
}
void thread_2()
{
if(ready.load(std::memory_order_relaxed))
{
std::atomic_thread_fence(std::memory_order_acquire);
std::cout<<"data="<<data<<std::endl;
}
}
Run Code Online (Sandbox Code Playgroud)
如果thread_2读取ready为true,则围栏确保data可以安全地读取,输出将是data=42.如果ready被读取false,那么你不能保证thread_1已经发布了相应的fence,所以线程2中的fence仍然不会提供必要的排序保证 - 如果ifin thread_2被省略,则访问data将是数据竞争并且未定义行为,即使有围栏.
澄清:A std::atomic_thread_fence(std::memory_order_release)通常相当于商店围栏,并且可能会这样实施.但是,一个处理器上的单个栅栏不保证任何内存排序:您需要在第二个处理器上使用相应的栅栏,并且您需要知道当执行获取栅栏时,释放栅栏的效果对于该第二个处理器是可见的.很明显,如果CPU A发出获取栅栏,然后5秒后CPU B发出释放栅栏,那么该释放栅栏就无法与获取栅栏同步.除非你有办法检查是否已经在另一个CPU上发布了栅栏,否则CPU A上的代码无法判断它是否在CPU B上的栅栏之前或之后发布了栅栏.
您使用原子操作检查是否已看到围栏的要求是数据竞争规则的结果:您无法从多个线程访问非原子变量而没有排序关系,因此您不能使用非原子操作用于检查排序关系的原子变量.
当然可以使用更强大的机制,例如互斥锁,但这会使单独的栅栏毫无意义,因为互斥体会提供栅栏.
轻松的原子操作可能只是普通CPU上的普通加载和存储,但可能需要额外的对齐要求以确保原子性.
编写为使用特定于处理器的防护的代码可以很容易地更改为使用C++ 0x防护,前提是用于检查同步的操作(而不是用于访问同步数据的操作)是原子的.现有代码很可能依赖于给定CPU上的普通加载和存储的原子性,但转换为C++ 0x将需要对这些检查使用原子操作以提供排序保证.
| 归档时间: |
|
| 查看次数: |
2296 次 |
| 最近记录: |