在执行线程的生命之后,是否可以将宽松的内存顺序效果扩展到?

时间:2015-09-01 17:52:37

标签: c++ multithreading c++11 concurrency atomic

让我们说在C ++ 11程序中,我们有一个名为 A 的主线程,它启动一个名为 B 的异步线程。在内部线程 B 中,我们对具有std::memory_order_relaxed内存顺序的原子变量执行原子存储。然后线程 A 与线程 B 连接。然后线程 A 启动另一个名为 C 的线程,该线程执行具有std::memory_order_relaxed内存顺序的原子加载操作。线程 C 加载的内容是否可能与线程 B 所写的内容不同?换句话说,松散的内存一致性是否延伸到线程的生命周期?

为了尝试这个,我编写了一个简单的程序并经过多次尝试运行它。该计划不报告不匹配。我在想,因为线程 A 在启动线程时强加了一个命令,不会发生不匹配。但是,我不确定。

#include <atomic>
#include <iostream>
#include <future>

int main() {

    static const int nTests = 100000;
    std::atomic<int> myAtomic( 0 );

    auto storeFunc = [&]( int inNum ){
        myAtomic.store( inNum, std::memory_order_relaxed );
    };

    auto loadFunc = [&]() {
        return myAtomic.load( std::memory_order_relaxed );
    };

    for( int ttt = 1; ttt <= nTests; ++ttt ) {
        auto writingThread = std::async( std::launch::async, storeFunc, ttt );
        writingThread.get();
        auto readingThread = std::async( std::launch::async, loadFunc );
        auto readVal = readingThread.get();
        if( readVal != ttt ) {
            std::cout << "mismatch!\t" << ttt << "\t!=\t" << readVal << "\n";
            return 1;
        }
    }

    std::cout << "done.\n";
    return 0;

}

2 个答案:

答案 0 :(得分:4)

在便携式线程平台通常为您提供指定内存可见性或放置显式内存障碍的能力之前,便携式同步只能通过显式同步(例如互斥)和隐式同步来完成。

通常,在创建线程之前,会设置一些数据结构,线程在启动时将访问这些数据结构。为了避免仅使用互斥锁来实现这种通用模式,线程创建被定义为隐式同步事件。加入一个线程然后查看它计算出的一些结果同样很常见。同样,为了避免仅使用互斥体来实现这种常见模式,加入线程被定义为隐式同步事件。

由于线程创建和结构被定义为同步操作,因此在该线程终止后,必须加入线程。因此,您将看到在线程终止之前必须发生的任何事情。对于更改某些变量然后创建线程的代码也是如此 - 新线程必须看到在创建之前发生的所有更改。线程创建或终止时的同步就像在互斥锁上进行同步一样。同步操作创建了这种确保内存可见性的排序关系。

正如SergeyA所提到的,你绝对不应该试图通过测试来证明多线程世界中的某些东西。当然,如果测试失败,那证明你不能依赖你测试的东西。但是,即使测试成功,您可以考虑测试它,但这并不意味着它不会在您未测试的某个平台,CPU或库上失败。你无法通过这种测试证明这样的东西是可靠的。

答案 1 :(得分:0)

如果你想测试这样的东西,可以使用模型检查器来探索测试用例的所有可能的执行(受到一些深奥的限制)。
http://plrg.eecs.uci.edu/c11modelchecker.html