C++多线程下shared_ptr::unique()能否判断线程独占所有权?
该用法完全不安全,属于典型的未定义行为。
核心原因
你当前实现的精简流程如下:
worker thread | rendering thread ----------------------------------- + ------------------------------------------------- std::shared_ptr<Bitmap> bitmap; | std::queue<std::shared_ptr<Bitmap>> copy; { | { std::scoped_lock lock(mutex); | std::scoped_lock lock(mutex); queue.push_back(bitmap); | std::swap(queue, copy); } | } ... | for(auto it = copy.begin(); it != copy.end(); ++it) | if (it->unique() == false) if (bitmap.unique()) | fillBitmap(*it); // writing to the bitmap bitmap->saveToDisk(); // reading
这个逻辑的本质漏洞有两点:
std::shared_ptr的引用计数增减本身是原子的,但unique()、use_count()的返回值只是调用瞬间的计数快照,你拿到返回值到基于这个值执行后续Bitmap访问操作之间存在时间窗口,其他线程完全可能在这个窗口内修改引用计数、访问Bitmap内存,触发TOCTOU(检查时间-使用时间)竞态。- 哪怕你读到
unique() == true,也无法保证此刻没有其他线程正在访问Bitmap对象:可能渲染线程已经拿到了shared_ptr的副本、已经进入fillBitmap()写流程但还没完成引用计数的递增,此时工作线程直接读Bitmap就会和写操作产生数据竞争,结果未定义。 - 从C++20标准开始,
unique()方法已被正式弃用,官方给出的核心理由就是该方法在多线程场景下几乎不存在安全的合法使用场景,极易被误用做跨线程同步依据。
替代方案评估
你计划改用的std::shared_ptr<std::pair<Bitmap, std::atomic<bool>>>方案是安全可行的:
- 渲染线程完成
fillBitmap()的所有写操作后,以release内存序将原子bool置为「填充完成」状态 - 工作线程轮询或等待原子bool值,以acquire内存序读到完成标记后,再执行
saveToDisk()读操作 - 原子变量的内存序同步保证可以完全规避读写竞态,不存在TOCTOU漏洞
如果不需要轮询场景,也可以直接用std::promise<void>配合std::future做一次性完成通知,代码语义会更清晰,不需要手动维护原子标记。
内容的提问来源于stack exchange,提问作者Anne Quinn
相关产品推荐
相关产品推荐

