互斥的两个操作是否必须序列化?以C++多线程文件描述符场景为例
问题解答
是的,必须保证close(fd)和use(fd)序列化执行,同时确保is_shutdown的状态检查与这两个操作的同步——不过不一定非要用传统临界区(比如mutex),但临界区是最直观、不易出错的实现方式。
为什么当前代码会出问题
你遇到的核心问题是缺乏线程间的同步关系:
- 虽然
is_shutdown是原子变量,但默认的内存序(memory_order_seq_cst)只能保证全局操作顺序,无法确保t2的load能及时看到t1的store更新; - 更关键的是,
close(fd)和use(fd)可能并发执行,这本身就属于对文件描述符的非法操作,即便is_shutdown检查正常,也会导致未定义行为; - 极端情况下,t1已经执行
close(fd),但由于CPU缓存延迟,t2的is_shutdown.load()仍读到false,进而执行use(fd),触发非法访问。
可行解决方案
方案一:互斥锁实现临界区(推荐)
用互斥锁将is_shutdown的修改、close(fd)与is_shutdown的检查、use(fd)包裹成互斥的临界区,从根本上避免并发问题:
#include <iostream> #include <atomic> #include <mutex> void close(int); void use(int); int create(); int main(){ std::atomic<bool> is_shutdown = false; std::mutex mtx; int fd = create(); std::thread t1([&](){ std::lock_guard<std::mutex> lock(mtx); is_shutdown.store(true); close(fd); }); std::thread t2([&]{ std::lock_guard<std::mutex> lock(mtx); if(is_shutdown.load()){ return; } use(fd); }); t1.join(); t2.join(); }
互斥锁的加解锁会自动建立线程间的同步:t1解锁后,t2加锁时能看到t1所有操作的最新结果,既解决了缓存一致性问题,也保证close和use不会同时执行。
方案二:原子内存序+条件变量(性能敏感场景)
如果对性能有要求,不想用互斥锁,可以通过release/acquire内存序建立同步,再配合条件变量让t1等待t2完成操作后再关闭fd:
#include <iostream> #include <atomic> #include <condition_variable> void close(int); void use(int); int create(); int main(){ std::atomic<bool> is_shutdown = false; std::atomic<bool> t2_done = false; std::condition_variable cv; std::mutex cv_mtx; int fd = create(); std::thread t1([&](){ is_shutdown.store(true, std::memory_order_release); // 等待t2完成use或提前退出 std::unique_lock<std::mutex> lock(cv_mtx); cv.wait(lock, [&]{ return t2_done.load(std::memory_order_acquire); }); close(fd); }); std::thread t2([&]{ if(is_shutdown.load(std::memory_order_acquire)){ t2_done.store(true, std::memory_order_release); cv.notify_one(); return; } use(fd); t2_done.store(true, std::memory_order_release); cv.notify_one(); }); t1.join(); t2.join(); }
release/acquire内存序确保t2能及时感知is_shutdown的更新,条件变量则保证t1不会在t2使用fd的过程中关闭它。
内容的提问来源于stack exchange,提问作者xmh0511
相关产品推荐
相关产品推荐

