C++:如何利用标准"as if"规则实现函数似在新线程运行却不创建线程?
Great question—this cuts straight to the flexibility of C++'s "as-if" rule and how standard library implementations can leverage it to meet requirements without literal adherence to the wording. Let's break this down step by step.
First, let's restate the key standard wording for clarity:
From [futures.async]/3 (C++ standard): When invoking
std::asyncwith thestd::launch::asyncpolicy, the implementation shall arrange for the functionfto execute as if it were running in a new execution thread.
The critical phrase here is "as if"—the standard doesn't mandate an actual operating system thread be created, only that all observable behavior of f's execution matches what would happen if it were running in a dedicated thread. That's the intended flexibility we can exploit.
Core Strategies to Implement This
Here are practical ways an implementation can simulate a new thread without creating one:
Simulate Thread-Local Storage (TLS) Isolation
Thread-local variables (thread_local) are a key part of thread-specific state. To makefthink it's in a new thread, the implementation can:- Save the current thread's TLS state for all relevant variables before executing
f. - Allocate a separate, isolated TLS storage region for
f's execution. - Switch to this simulated TLS context before running
f, then switch back oncefcompletes.
Fromf's perspective, it's accessing unique thread-local data, just like it would in a real thread.
- Save the current thread's TLS state for all relevant variables before executing
Use User-Space Coroutines/Execution Units
Instead of spinning up an OS thread, the implementation can wrapfin a user-space coroutine (e.g., using C++20 coroutines or a custom user-space scheduler). When the caller waits on the returnedstd::future(viaget()orwait()), the scheduler runs the coroutine in the current thread—but with important safeguards:- Assign a unique thread ID to the coroutine (returned by
std::this_thread::get_id()) that doesn't collide with any real OS thread IDs. - Ensure the coroutine's execution is isolated from the caller thread's other work (e.g., no unexpected preemption that would break the "as if" thread behavior).
- Assign a unique thread ID to the coroutine (returned by
Isolate Thread-Specific Execution State
Beyond TLS, threads have other observable state like signal masks, scheduling priorities, and CPU affinity. The implementation can:- Save the caller thread's current state before running
f. - Apply a fresh, default state (empty signal mask, default priority, etc.) for
f's execution. - Restore the original state once
ffinishes.
This makesf's execution environment indistinguishable from a new thread's.
- Save the caller thread's current state before running
Quick Example: Simulated TLS Isolation
Here's a simplified pseudocode sketch to illustrate the TLS approach:
// Thread-local variable that f might access thread_local int thread_specific_counter = 0; std::future<void> simulated_async(std::launch policy, auto&& f) { if (policy != std::launch::async) { return std::async(policy, std::forward<decltype(f)>(f)); } // Save current thread's TLS state int original_counter = thread_specific_counter; // Initialize simulated TLS for f's execution thread_specific_counter = 0; // Run f in the current thread, but with isolated TLS std::invoke(std::forward<decltype(f)>(f)); // Restore original TLS state thread_specific_counter = original_counter; // Return a ready future (simplified for demonstration) std::promise<void> p; p.set_value(); return p.get_future(); }
In this example, f will see thread_specific_counter start at 0, just like it would in a new thread—even though we're reusing the caller's OS thread.
Key Takeaway
The "as-if" rule is all about behavioral equivalence, not literal implementation. As long as every observable aspect of f's execution matches what would occur in a real new thread, the implementation is compliant. This allows for optimizations like reusing existing threads (via coroutines or thread pools) while still satisfying the standard's requirements.
内容的提问来源于stack exchange,提问作者KnowItAllWannabe

