C++使用ppl的concurrent_vector出现线程间状态不一致问题
问题根因与解决
你遇到的现象的直接原因
你现在的代码有两个核心问题:
- 消费者线程的
saveImages逻辑没有循环:该函数只会执行一次,若执行时机早于生产者第一次push_back操作,自然会读取到容器为空,执行Sleep(20)后线程直接退出,不会再后续重试读取容器内容,和你用不用并发容器没有关系。 - ppl的
concurrent_vector的empty()方法仅返回调用瞬间的容器状态,本身不提供同步保障:即使你读到empty() == false,在你后续执行读取操作的间隙,也可能出现容器状态变化,反过来也成立。
关于并发容器是否需要额外同步的解答
所有标准/第三方实现的并发容器,都仅保证容器自身结构的并发安全,也就是多线程同时操作容器时,不会出现容器结构损坏、内存越界、元素重复/丢失插入这类底层错误,但业务逻辑层面的时序同步依然需要你自行通过同步原语实现。
以concurrent_vector为例,它只保证多线程同时调用push_back是安全的,但它没法帮你保证「生产者先推数据、消费者再读数据」的业务时序,也没法保证你调用empty()后到读数据的间隙容器状态不变,这些都需要你自己加同步逻辑。
修复后的核心代码示例
你可以通过条件变量实现生产者消费者的时序同步,参考修改如下:
首先新增同步控制变量:
#include <mutex> #include <condition_variable> // 原有全局变量 concurrent_vector<dataElm> ResultImage; bool continue_recording = false; // 新增同步变量 std::mutex g_mtx; std::condition_variable g_cv;
生产者推送数据后通知消费者:
int AcquireImages(CameraPtr pCam){ continue_recording = true; pCam->BeginAcquisition(); int imageCnt = 0; while (continue_recording == true) { ImagePtr _p = pCam->GetNextImage(1000); imageCnt = imageCnt + 1; dataElm obj = constructelm(_p, &loc, imageCnt - 1); ResultImage.push_back(obj); cout << "is buffer empty? " << ResultImage.empty() << endl; g_cv.notify_one(); // 新增:通知消费者有新数据写入 } g_cv.notify_all(); // 新增:生产者退出前通知所有等待的消费者 //... }
消费者修改为循环等待逻辑:
void saveImages() { while(true) { std::unique_lock<std::mutex> lock(g_mtx); // 等待直到有数据 或者 生产者已经停止录制 g_cv.wait(lock, []{ return !ResultImage.empty() || !continue_recording; }); // 生产者已停止 且 容器内无剩余数据,退出线程 if(ResultImage.empty() && !continue_recording) { break; } // 这里执行你的图像保存逻辑 // 注意:concurrent_vector已写入的元素地址不会失效,可通过下标安全访问 lock.unlock(); // 处理完当前批次数据后可以根据需要加适当休眠避免空转 } }
额外注意事项
- 不要在多线程场景下用
concurrent_vector的迭代器遍历容器,迭代器不保证并发安全,优先用下标访问已经完成写入的元素。 - 如果你需要在消费者读取后移除已处理的元素,
concurrent_vector本身不支持线程安全的弹出操作,建议改用并发队列实现,比如PPL的concurrent_queue,更适配生产者消费者场景。
内容的提问来源于stack exchange,提问作者emma
相关产品推荐
相关产品推荐

