C++多线程调用call_once程序在在线编译器超时求助
问题:多线程调用
std::call_once()的C++程序在在线编译器中超时 我写了个多线程环境下调用std::call_once()的C++程序,本来是想演示如果首次调用call_once()失败,后续调用会被触发的逻辑,但这个程序在Coliru和Ideone这两个在线编译器里都卡超时了。现在把源码贴出来,麻烦帮忙看看问题出在哪~
我的源码(call_once_xcp.cpp)
#include <mutex> /// once_flag, call_once() #include <thread> /// thread #include <exception> /// runtime_error #include <iostream> /// cout using namespace std; std::once_flag flag; void may_throw() { throw std::runtime_error("First call failed!"); } void thread_func() { try { std::call_once(flag, may_throw); cout << "Successfully executed call_once\n"; } catch (const std::exception& e) { cout << "Caught exception: " << e.what() << endl; } } int main() { std::thread t1(thread_func); std::thread t2(thread_func); std::thread t3(thread_func); t1.join(); t2.join(); t3.join(); return 0; }
问题排查与解决办法
其实超时的原因很好理解:当std::call_once()的首次调用抛出异常时,std::once_flag的状态会被重置成"未完成"——这正是你想演示的点,但在线编译器的线程调度机制,会让多个线程反复尝试执行may_throw(),陷入抛出异常→捕获→再次尝试的死循环,时间一长就触发了超时限制。
要解决这个问题,我们可以加个标记,确保首次失败后,后续调用不再重复抛出异常,避免循环消耗时间:
修正后的代码
#include <mutex> #include <thread> #include <exception> #include <iostream> #include <atomic> std::once_flag flag; // 用原子变量标记是否已经失败过,保证多线程下的线程安全 std::atomic<bool> has_failed{false}; void may_throw() { // 如果已经失败过,直接返回,不再抛异常 if (has_failed.load(std::memory_order_acquire)) { return; } // 标记为已失败,这里用release内存序确保其他线程能看到这个更新 has_failed.store(true, std::memory_order_release); throw std::runtime_error("First call failed!"); } void thread_func() { try { std::call_once(flag, may_throw); std::cout << "Successfully executed call_once\n"; } catch (const std::exception& e) { std::cout << "Caught exception: " << e.what() << '\n'; } } int main() { std::thread t1(thread_func); std::thread t2(thread_func); std::thread t3(thread_func); t1.join(); t2.join(); t3.join(); return 0; }
为什么这样改?
- 我们加了一个
std::atomic<bool>的has_failed变量,原子操作能保证多线程环境下的状态读写是线程安全的,不会出现竞态条件。 - 第一个线程执行
may_throw()时,会先把has_failed设为true,然后抛出异常;后续线程再进入may_throw()时,看到has_failed已经是true,就直接返回,这样std::call_once()就能顺利完成执行,不会再反复触发异常,自然也就不会超时了。
另外提一句,别想着手动重置std::once_flag——C++标准里根本没给它公开的重置接口,硬改属于未定义行为,绝对不能碰,用原子变量标记状态才是合规又安全的做法。
内容的提问来源于stack exchange,提问作者SSteven
相关产品推荐
相关产品推荐

