Boost::Coroutine2与C++20 CoroutineTS:特定场景下该如何选型?
When to Choose C++20 Stackless Coroutines vs Boost.Coroutine2 Stackful Coroutines
Great question! The core difference between these two coroutine implementations—stackless vs stackful—creates clear scenarios where one is a far better fit than the other. Let’s break them down:
Best for C++20 Stackless Coroutines (CoroutineTS)
- High-concurrency, low-overhead systems
Stackless coroutines have tiny memory footprints (just a small struct to track execution state, often tens of bytes per coroutine). This makes them ideal for handling thousands or even millions of concurrent tasks, like async IO servers. For example, usingstd::generatorto produce infinite sequences without heavy stack allocations, or chaining async operations withco_awaitto avoid callback hell. - Standard-compliant, portable codebases
If you want to avoid third-party dependencies (like Boost) and lean into the modern C++ ecosystem, stackless coroutines are part of the standard library. They integrate seamlessly with C++20+ features likestd::future,std::jthread, and emerging async libraries—no extra build steps or versioning headaches to manage. - Simple state machines or iterative workflows
The compiler translates stackless coroutines into a compile-time state machine, making them extremely efficient for tasks like parsers, iterators, or linear workflow steps. For instance:
This code runs with almost no overhead compared to a hand-written state machine.generator<int> count_up(int start) { while (true) { co_yield start++; } } - Memory-constrained environments
Embedded systems or devices with limited RAM benefit from stackless coroutines, as they don’t require allocating dedicated stack space (unlike stackful coroutines, which need a separate stack per instance).
Best for Boost.Coroutine2 Stackful Coroutines
- Complex nested logic needing arbitrary suspension
Stackful coroutines let you suspend execution at any point in the call stack, not just atco_await/co_yieldkeywords. This is critical for tasks like recursive algorithms (e.g., a depth-first search that suspends when it finds a result) or code that calls third-party libraries where you can’t addco_awaitannotations. For example:
Replicating this with stackless coroutines would require rewriting the recursive logic entirely, which is far more work.pull_type dfs_coroutine([](push_type& sink) { std::function<void(Node*)> dfs = [&](Node* node) { if (!node) return; sink(node->value); // Suspend here, even inside a nested lambda dfs(node->left); dfs(node->right); }; dfs(root_node); }); - Migrating legacy synchronous code
If you have existing synchronous, blocking code that you want to make asynchronous without a full rewrite, stackful coroutines are perfect. You can wrap the entire blocking function in a coroutine and suspend at blocking points—no need to refactor every function to useco_await. - User-level threads (fibers) or full call stack preservation
If you need to mimic lightweight threads that preserve the entire call stack state when suspended (e.g., game AI logic that pauses mid-execution, or custom task schedulers), stackful coroutines are the way to go. They save the full stack context, so resuming picks up exactly where you left off, including all local variables in nested function calls. - Existing Boost Coroutine codebases
If you’re already using older versions of Boost.Coroutine, upgrading to Boost.Coroutine2 is a smooth transition. You won’t have to rewrite your code to fit the C++20 coroutine model, saving significant time and effort.
内容的提问来源于stack exchange,提问作者Nisal Dissanayake
相关产品推荐
相关产品推荐

