为何在共享内存中使用std::atomic.wait()会出现挂起?
为什么跨进程使用std::atomic的wait/notify会挂起?
你的代码出现挂起的核心原因是:C++标准库的std::atomic<T>::wait()和notify_all()并不支持跨进程同步。
这些原子等待/通知操作的底层实现依赖于进程内部的同步结构(比如进程私有条件变量),即使原子变量本身位于共享内存中,不同进程的wait调用也无法感知到其他进程的notify信号——因为每个进程的wait操作绑定的是自己进程内的同步上下文,跨进程的notify无法触发其他进程的wait唤醒。这也是为什么忙等待能正常运行:忙等待是直接轮询共享内存中的原子变量值,不依赖进程内的同步结构。
除此之外,你的代码还有两个潜在问题会加剧这个问题:
- 原子变量类型不匹配:
SharedMemory里的last_update_nanotime是std::atomic<std::uint32_t>,但发送端赋值的是uint64_t类型的纳秒时间戳。uint32_t最大只能存储约42亿的数值,而当前系统的纳秒时间戳早已超过这个范围,赋值时会被截断,可能导致多次更新后原子变量的值重复,即使wait能跨进程工作,也可能因为值未变化而无法唤醒。 - 未初始化原子变量:
find_or_construct<SharedMemory>会默认初始化结构体成员,std::atomic<std::uint32_t>的默认值是0,接收端初始读取的是0,而发送端第一次赋值的是截断后的时间戳,但若截断后的值恰好还是0(概率极低,但存在可能),wait会一直挂起。
修复方案
要实现跨进程的同步,需要使用Boost Interprocess提供的跨进程同步原语,比如boost::interprocess::mutex和boost::interprocess::condition_variable。同时修正原子变量的类型匹配问题:
- 修改
shared_memory.hpp,添加跨进程同步对象:
// shared_memory.hpp #include <atomic> #include <cstdint> #include <boost/interprocess/sync/interprocess_mutex.hpp> #include <boost/interprocess/sync/interprocess_condition.hpp> struct SharedMemory { char message[100]; std::atomic<uint64_t> last_update_nanotime; // 改为uint64_t避免截断 boost::interprocess::interprocess_mutex mutex; boost::interprocess::interprocess_condition cond; };
- 修改sender.cpp,使用跨进程条件变量通知:
// sender.cpp #include <iostream> #include <boost/interprocess/managed_mapped_file.hpp> #include "shared_memory.hpp" #include <thread> #include <chrono> int main() { std::string filename = "/tmp/boost_shared_memory"; boost::interprocess::managed_mapped_file mfile( boost::interprocess::open_or_create, filename.c_str(), 2000); // 增大内存以容纳同步对象 auto* shared_mem = mfile.find_or_construct<SharedMemory>("SharedMemory")(); while(true) { std::cout << "Locking & writing" << std::endl; { boost::interprocess::scoped_lock<boost::interprocess::interprocess_mutex> lock(shared_mem->mutex); uint64_t nanotime = std::chrono::duration_cast<std::chrono::nanoseconds>( std::chrono::system_clock::now().time_since_epoch()).count(); shared_mem->last_update_nanotime = nanotime; shared_mem->cond.notify_all(); // 跨进程通知 } std::cout << "Set time " << shared_mem->last_update_nanotime << std::endl; std::this_thread::sleep_for(std::chrono::seconds(1)); } }
- 修改receiver.cpp,使用跨进程条件变量等待:
// receiver.cpp #include <iostream> #include <boost/interprocess/managed_mapped_file.hpp> #include "shared_memory.hpp" #include <thread> #include <chrono> int main() { std::string filename = "/tmp/boost_shared_memory"; boost::interprocess::managed_mapped_file mfile( boost::interprocess::open_or_create, filename.c_str(), 2000); auto* shared_mem = mfile.find_or_construct<SharedMemory>("SharedMemory")(); uint64_t last_update_nanotime = shared_mem->last_update_nanotime; std::cout << "is lock free? " << shared_mem->last_update_nanotime.is_lock_free() << std::endl; while(true) { std::cout << "Waiting" << std::endl; { boost::interprocess::scoped_lock<boost::interprocess::interprocess_mutex> lock(shared_mem->mutex); // 等待直到原子变量值变化,避免虚假唤醒 while (shared_mem->last_update_nanotime == last_update_nanotime) { shared_mem->cond.wait(lock); } last_update_nanotime = shared_mem->last_update_nanotime; std::cout << "Received time: " << last_update_nanotime << std::endl; std::cout << "Received message: " << shared_mem->message << std::endl; } } }
关键说明
- Boost Interprocess的同步原语是专门为跨进程共享内存设计的,它们的底层实现依赖于系统级的跨进程同步机制(比如POSIX的命名互斥锁/条件变量),可以在不同进程间正常工作。
- 使用条件变量时必须配合互斥锁,并且要用循环检查条件,这是为了避免系统级的虚假唤醒问题,是跨进程同步的标准做法。
内容的提问来源于stack exchange,提问作者Thomas Johnson
相关产品推荐
相关产品推荐

