call_once异常场景下的行为:函数未执行完成抛异常时once_flag的值是什么?
std::call_once in Exception Scenarios: Behavior and once_flag State Let's dive into how std::call_once behaves when the target function throws an exception before completing, and what that means for the associated std::once_flag.
Behavior When the Target Function Throws
The core purpose of std::call_once is to guarantee a function runs exactly once across all threads. But if that function throws an exception before finishing execution, this counts as a failed attempt. Here's the breakdown:
- The exception gets propagated straight back to the thread that initiated the
call_oncecall. - Any other threads blocked waiting on the same
once_flagwill wake up, and one of them will retry running the target function. - This cycle repeats until one invocation of the function completes successfully (no exceptions thrown) — at that point,
call_oncemarks the flag as "done", and no further invocations will happen.
What's the "State" of once_flag After a Failed Execution?
First, a quick clarification: std::once_flag is an opaque type — you can't directly read its internal value or state via any public API. But we can infer its state from how call_once behaves:
- If the target function throws before completing, the
once_flagstays in its initial, untriggered state. It does NOT get marked as "completed". - This means subsequent calls to
std::call_oncewith the same flag will still attempt to run the target function, just like the first time.
Example to Illustrate
Here's a simple code snippet that shows this behavior in action:
#include <iostream> #include <mutex> #include <stdexcept> #include <thread> std::once_flag g_flag; int g_attempts = 0; void failing_function() { g_attempts++; std::cout << "Attempt " << g_attempts << ": Throwing exception\n"; throw std::runtime_error("Something went wrong!"); } void working_function() { g_attempts++; std::cout << "Attempt " << g_attempts << ": Executing successfully\n"; } int main() { // First call: function throws try { std::call_once(g_flag, failing_function); } catch (const std::exception& e) { std::cout << "Caught exception: " << e.what() << "\n"; } // Second call: since flag wasn't marked as done, we try again with a working function std::call_once(g_flag, working_function); std::cout << "Total attempts made: " << g_attempts << "\n"; return 0; }
When you run this, the output will be:
Attempt 1: Throwing exception
Caught exception: Something went wrong!
Attempt 2: Executing successfully
Total attempts made: 2
This confirms that the first failed execution didn't mark the flag as completed — the second call_once happily runs the working function.
Key Takeaways
std::call_onceonly considers the target function "done" when it executes fully without throwing.- Exceptions during execution reset the process: the flag stays untriggered, and future calls will retry.
- You can't inspect the
once_flagdirectly, but its behavior tells you everything you need to know about its state.
内容的提问来源于stack exchange,提问作者Thomas Martin

