这里需要内存栅栏吗?

Posted

技术标签:

【中文标题】这里需要内存栅栏吗?【英文标题】:Are memory fences required here? 【发布时间】:2017-01-02 12:58:05 【问题描述】:

考虑一下这段代码(摘自Simple-Web-Server,但回答这个问题不需要了解库):

HttpServer server;
thread server_thread;

server.config.port = 8080;
server.default_resource["GET"] = [](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request) 
    string content = "Hello world!"
    *response << "HTTP/1.1 200 OK\r\nContent-Length: " << content.size() << "\r\n\r\n" << content;
;

server_thread = thread([&server]() 
    server.start();
);

HttpServer::default_resource 是一个 std::unordered_map,据我了解,它不是线程安全的。 port 是一个无符号短。

假设我对 C++ 内存栅栏的理解是正确的,server,正如新线程所见,可能不是处于有效状态,因为主线程可能没有将对 portdefault_resource 的更改写入可从其他线程访问的内存。因此,server.start() 可能无法正常工作。

要解决这个问题,我必须通过添加到 atomic_thread_fences 来更改代码:

HttpServer server;
thread server_thread;

server.config.port = 8080;
server.default_resource["GET"] = [](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request) 
    string content = "Hello world!"
    *response << "HTTP/1.1 200 OK\r\nContent-Length: " << content.size() << "\r\n\r\n" << content;
;

atomic_thread_fence(memory_order_release);

server_thread = thread([&server]() 
    atomic_thread_fence(memory_order_acquire);
    server.start();
);

我的理解是否正确,atomic_thread_fences 都是必要的吗?

【问题讨论】:

从刚刚创建的线程的角度来看,在线程初始化之前排序的所有父线程执行的操作必须是完整的。因此,在这种情况下,不需要线程围栏,因为在调用 std::thread::thread() 之前已经初始化了服务器。 您似乎将实际线程(并发概念)与线程处理程序对象(C++ 对象)混淆了。 【参考方案1】:

30.3.1.2 线程构造函数

template <class F, class ...Args> 
explicit thread(F&& f, Args&&... args);

同步:构造函数的调用完成 与 f 副本的调用开始同步。

换句话说:当线程函数被调用时,它会与父线程中发生的一切同步,直到在父线程中构造std::thread

不需要这种明确的内存屏障/栅栏。

【讨论】:

OPs 代码难道不是因为server 作为参数传递而工作,并且在那时必须执行所有先前具有影响该变量的副作用的代码吗?例如,假设 server 没有作为参数传递,而是一个全局变量——如果我理解正确,那么这会导致同步问题。引用的文字只是说线程构造函数必须在线程回调之前执行。 即使使用 pthread_create() 代替 std::thread,也不需要内存栅栏。经验法则是:使用互斥锁或其他同步原语创建线程或等待另一个线程隐式发出所需的栅栏。你不必自己做。不幸的是,我无法为此提供证明链接。 加入线程时会不会一样,即调用thread::join()后不需要显式栅栏? @Bernard:是的,加入线程会隐式发出必要的栅栏,以确保该线程的内存存储对执行连接的线程可见。

以上是关于这里需要内存栅栏吗?的主要内容,如果未能解决你的问题,请参考以下文章

内存栅栏

内存栅栏:获取/加载和释放/存储

记忆栅栏是如何工作的?

C# volatile 变量:内存栅栏 VS。缓存

是否应该为互斥锁获取-交换循环(或队列获取-加载循环)组合内存栅栏,还是应该避免?

cuda 线程栅栏