I'm trying to understand C++ coroutines. My expectation in the example below would be, that each asio::post will switch the context/thread to the given threadpool. But something very strange happens. I get the following output:
thread0: 7f0239bfb3c0
thread2: 7f02391f6640 (tp2)
thread3: 7f02387f5640 (tp)
thread4: 7f02387f5640 (tp2)
thread5: 7f02373f3640 (tp)
thread6: 7f02373f3640 (tp2)
thread7: 7f0235ff1640 (tp)
done
So the thread-id of 3 and 4 are the same, but they should run on a different context/threadpool. And 3, 5 and 7 should have the same ID since it's the same context (with just one thread). I assume that I understand some concept wrong. Can you give me a hint?
Thanks
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <iostream>
boost::asio::thread_pool tp {1};
boost::asio::thread_pool tp2 {10};
boost::asio::awaitable<void> test() {
std::cout << "thread2: " << boost::this_thread::get_id() << std::endl;
co_await boost::asio::post(tp, boost::asio::use_awaitable);
std::cout << "thread3: " << boost::this_thread::get_id() << std::endl;
co_await boost::asio::post(tp2, boost::asio::use_awaitable);
std::cout << "thread4: " << boost::this_thread::get_id() << std::endl;
co_await boost::asio::post(tp, boost::asio::use_awaitable);
std::cout << "thread5: " << boost::this_thread::get_id() << std::endl;
co_await boost::asio::post(tp2, boost::asio::use_awaitable);
std::cout << "thread6: " << boost::this_thread::get_id() << std::endl;
co_await boost::asio::post(tp, boost::asio::use_awaitable);
std::cout << "thread7: " << boost::this_thread::get_id() << std::endl;
}
int main() {
std::cout << "thread0: " << boost::this_thread::get_id() << std::endl;
boost::asio::co_spawn(tp2, &test, [](std::exception_ptr e) { std::cout << "done" << std::endl; });
tp2.join();
tp.join();
}
I have a near identical answer up here: asio How to change the executor inside an awaitable?
I'll leave the explanation there, but your code can act as expected like this:
#include <boost/asio.hpp>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <thread>
namespace asio = boost::asio;
asio::thread_pool tp1{1}, tp2{1};
static inline auto trace(auto msg) {
static std::mutex mx;
std::lock_guard lg(mx);
std::cout //
<< "trace " << msg << ": thread " << std::hex << std::setw(2)
<< std::setfill('0')
<< (std::hash<std::thread::id>{}(std::this_thread::get_id()) % 256)
<< std::endl;
}
asio::awaitable<void> test() {
trace(2);
auto a1 = asio::bind_executor(tp1.get_executor(), asio::use_awaitable);
auto a2 = asio::bind_executor(tp2.get_executor(), asio::use_awaitable);
co_await post(a1); trace(3);
co_await post(a2); trace(4);
co_await post(a1); trace(5);
co_await post(a2); trace(6);
co_await post(a1); trace(7);
}
int main() {
post(tp1, [] { trace("tp1"); });
post(tp2, [] { trace("tp2"); });
co_spawn(tp2, test, [](std::exception_ptr const&) {
std::cout << "done" << std::endl;
});
tp2.join();
tp1.join();
}
Prints e.g.
trace tp2: thread 33
trace 2: thread 33
trace tp1: thread d6
trace 3: thread d6
trace 4: thread 33
trace 5: thread d6
trace 6: thread 33
trace 7: thread d6
done