如何阻塞程序执行直至回调函数完成运行?
解决异步回调未完成时main函数提前退出的问题
这是个很常见的异步操作同步等待场景,咱们可以用C++标准库的同步原语来实现阻塞,直到回调函数执行完成。下面给你两种实用的解决方案:
方案一:使用std::condition_variable + std::mutex(推荐,适合重复调用场景)
这种方式需要给InterfaceColliseo类添加几个同步成员变量,用来跟踪回调是否完成,并通知主线程等待。
第一步:修改InterfaceColliseo类定义
在类的头文件里添加同步相关的成员:
#include <mutex> #include <condition_variable> class InterfaceColliseo { // 保留你原有的成员和方法... private: std::mutex m_callbackMutex; std::condition_variable m_callbackCondVar; bool m_callbackCompleted = false; public: // 新增方法:阻塞直到回调完成 void waitUntilCallbackDone() { std::unique_lock<std::mutex> lock(m_callbackMutex); // 等待回调完成的标志变为true,避免虚假唤醒 m_callbackCondVar.wait(lock, [this](){ return m_callbackCompleted; }); // 重置标志,方便后续重复调用getImage m_callbackCompleted = false; } // 修改原有的dataReceived回调方法 void dataReceived(std::shared_ptr<IData> data) { // 保留你原有的业务逻辑... if (!data->isValid()) return; const unsigned char* twoDImageData = data->get2DImageData(); int width2DImageData = data->getWidth2DImageData(); int height2DImageData = data->getHeight2DImageData(); const unsigned char* disparityData = data->getDisparityData(); int widthDisparityData = data->getWidthDisparityData(); int heightDisparityData = data->getHeightDisparityData(); if(IMAGE) { saveImage(twoDImageData, width2DImageData, height2DImageData, disparityData, widthDisparityData, heightDisparityData); IMAGE = false; } if(ACQUISATION){ QList<GstObservationBasic> detectorData = data->getObstaclesData(); getObstacles(detectorData); } // 回调完成后,更新标志并通知等待的主线程 std::lock_guard<std::mutex> lock(m_callbackMutex); m_callbackCompleted = true; m_callbackCondVar.notify_one(); } };
第二步:修改main函数
调用getImage后,直接调用新增的等待方法即可阻塞:
int main(){ InterfaceColliseo interface; IMAGE = true; interface.getImage(); interface.waitUntilCallbackDone(); // 这里会阻塞到回调执行完成 return 0; }
方案二:使用std::promise + std::future(适合一次性调用场景)
如果只需要单次等待,这种方式更轻量,不需要修改类的成员变量(如果允许修改类的话可以更优雅,否则可以用全局变量应急)。
优雅版(修改InterfaceColliseo类)
给getImage方法添加一个参数,用来传递完成通知的promise:
// 在InterfaceColliseo类里添加成员 std::shared_ptr<std::promise<void>> m_completionPromise; // 修改getImage方法 void InterfaceColliseo::getImage(std::shared_ptr<std::promise<void>> completionPromise) { m_completionPromise = completionPromise; // 保留原有的getImage逻辑... } // 修改dataReceived方法 void InterfaceColliseo::dataReceived(std::shared_ptr<IData> data) { // 保留原有的业务逻辑... // 回调完成后触发promise if (m_completionPromise) { m_completionPromise->set_value(); m_completionPromise.reset(); } }
然后修改main函数:
#include <future> int main(){ InterfaceColliseo interface; IMAGE = true; auto completionPromise = std::make_shared<std::promise<void>>(); auto future = completionPromise->get_future(); interface.getImage(completionPromise); future.wait(); // 阻塞直到回调触发promise // 可选:添加超时等待,避免无限阻塞 // if (future.wait_for(std::chrono::seconds(5)) == std::future_status::timeout) { // // 处理超时逻辑 // } return 0; }
应急版(不修改类,用全局变量)
如果无法修改InterfaceColliseo的代码,可以临时用全局变量传递promise(不推荐长期使用,因为全局变量线程不安全):
#include <future> std::promise<void>* g_completionPromise = nullptr; // 修改你的dataReceived回调 void InterfaceColliseo::dataReceived(std::shared_ptr<IData> data) { // 保留原有的业务逻辑... // 确保是本次main调用触发的回调 if (IMAGE && g_completionPromise) { g_completionPromise->set_value(); g_completionPromise = nullptr; } } int main(){ InterfaceColliseo interface; IMAGE = true; std::promise<void> completionPromise; g_completionPromise = &completionPromise; auto future = completionPromise.get_future(); interface.getImage(); future.wait(); return 0; }
注意事项
- 记得处理异常情况:比如回调可能因为错误不会触发,这时候可以给等待添加超时,避免程序永久阻塞。
- 如果是多线程环境,要确保所有同步操作的线程安全,避免数据竞争。
内容的提问来源于stack exchange,提问作者Kamel Ghalgaoui
相关产品推荐
相关产品推荐

