松弛的内存顺序效果是否可以扩展到执行线程的生命周期之后?

Does relaxed memory order effect can be extended to after performing-thread's life?

本文关键字:线程 执行 生命 扩展到 周期 之后 内存 顺序 是否      更新时间:2023-10-16

假设在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;
}

在可移植线程平台通常为您提供指定内存可见性或设置显式内存屏障的能力之前,可移植同步只能通过显式同步(如互锁)和隐式同步来完成。

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

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

正如SergeyA所提到的,你绝对不应该试图通过测试来证明多线程世界中的某些东西。当然,如果一个测试失败了,那就证明你不能依赖你所测试的东西。但是,即使一个测试通过了你所能想到的所有测试方法,这并不意味着它不会在你没有测试过的平台、CPU或库上失败。你永远不能通过这种测试来证明这样的事情是可靠的。

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