2017-04-03 20 views
1

我理解C++ 11中内存排序的基本规则,特别是release-acquire排序。我在两个线程之间共享了一大块内存,我不需要原子性,但是要确保线程完成的所有更改最终都可以在另一个线程中看到,尤其是在具有宽松内存模型的平台上。不锁定线程之间的数据可见性

可以简单地使用原子防护变量var来触发内存同步吗?例如,

std::atomic<bool> guardVar; 
char *shared_mem=get_shared_mem(); 

(thread 1) 
while(true) { 
    happens_many_things(); 
    do_whatever_I_want_with_shared_mem(); 
    guardVar.store(0, std::memory_order_release); 
} 

(in thread 2) 
while(true) { 
    guardVar.load(std::memory_order_acquire); 
    read_shared_mem_no_problem_if_inconsistent(); 
} 

再次,这是不是一个问题,如果线程2 do_whatever_I_want_with_shared_mem(),我只是想确保我得到线程1所写的所有变化的中间读“半就绪”状态在一个明确的点之后。

基于this article它应该工作,但我没有在网络上看到这样的解决方案,并且不容易测试它是否真的按照我的意图进行测试。

可以吗?如果是这样,有没有更优雅的方式?

回答

1

它不是一个问题,如果线程2 do_whatever_I_want_with_shared_mem()

这是一个错误,你不能被多个线程访问共享内存中读取一个“半准备”状态,如果一个他们正在修改数据。 C++标准将其称为数据竞争,并导致未定义的行为。

两个线程之间的访问需要同步,但是您使用std::atomic的方式不正确。线程1中的store_release紧接着再次访问相同的数据。 load_acquire也是如此;两个操作之间没有同步,因此您正在处理数据竞争。

为了确保您的共享存储器由一个线程一次只能访问,guardVar技术上可以使用这样的:

std::atomic<bool> guardVar{false}; 

(thread 1) 
while(true) { 

    while (guardVar.exchange(true, std::memory_order_acquire)); // LOCK 

    happens_many_things(); 
    do_whatever_I_want_with_shared_mem(); 

    guardVar.store(false, std::memory_order_release); // UNLOCK 
} 

(in thread 2) 
while(true) { 

    while (guardVar.exchange(true, std::memory_order_acquire)); // LOCK 

    read_shared_mem_no_problem_if_inconsistent(); 

    guardVar.store(false, std::memory_order_release); // UNLOCK 
} 

但由于这是使用std::atomic在一个相当低效的方式互斥(注意纺),你真的应该使用std::mutex

更新:

它仍然可以使用您的共享我mory没有锁定,但是你有责任确保在共享内存中访问的每个单独对象都是无数据竞争的(对象有资格获得std::atomic)。

然后,您或多或少地获得您在第二个线程可能看到“半准备”状态(某些对象已更新,其他则不是)的问题中描述的行为。如果没有同步,第二个线程无法真正知道第一个线程的更新何时完成,但至少可以安全地同时读取/写入数据竞争自由对象。

+0

嗯。你是对的,这确实是一个未定义的行为。 – Ferenc

+0

嗯。你说得对,这是标准确实是一个未定义的行为。 想象一下,我模拟一个帧缓冲区,一个线程在随机访问中连续写入的内容,另一个线程想定期读出整个缓冲区来处理它。撕裂不是问题。 我怎样才能解决这个没有任何类型的锁定在任何线程? – Ferenc

+0

@Ferenc这个场景确实是你问题的一部分..我会更新答案 – LWimsey