You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 11:08:28