I try to understand how iocontext works and created a simple program to understand its better step by step. As you can see, I even not to run ioc.run(). And I believed this code would do nothing. But when I run it and type something like
aaa;bbb;ccc;ddd
then get something like
ThreadID = ThreadID = 17364; bbbThreadID = 72872; ccc
ThreadID = 5428025840; aaa
; ddd
And under debug I see a lot of (about 40) threads.
What does it mean? Is it the right behaviour? And why does it work at all?
#include <iostream>
#define _WIN32_WINNT 0x0A00
#include <boost/asio.hpp>
#include <sstream>
std::vector<std::string> parseInput(const std::string& input)
{
std::vector<std::string> result;
std::istringstream iss(input);
std::string token;
while (std::getline(iss, token, ';'))
{
result.push_back(token);
}
return result;
}
int main()
{
try
{
boost::asio::io_context ioc;
for (;;)
{
std::string input;
std::getline(std::cin, input);
std::vector<std::string> tokens = parseInput(input);
if (tokens.empty())
break;
for (const std::string& token : tokens)
{
boost::asio::post([token]()
{
std::cout << "ThreadID = " << std::this_thread::get_id() << "; " << token << std::endl;
});
}
}
}
catch (std::exception& exc)
{
std::cerr << exc.what() << std::endl;
}
return 0;
}
Win12, boost::asio 1.82.0, MSVC 2022 (I used different compilers, c++ 14 and c++ 20)
You're posting to the system executor, which is usually implemented as a global thread pool.
You use the overload of asio::post
that doesn't specify an explicit executor. Therefore, the associated executor will be used. No executor has been associated with the posted handler.
get_associated_executor
will return the default executor, which, if unspecified, will itself default to asio::system_executor{}
.
Using an explicit executor:
asio::post(ioc.get_executor(), handler);
ADL makes it so that you can probably use unqualified:
post(ioc.get_executor(), handler);
Next up, as a convenience Asio supports posting “to” an execution context, in which case its default executor will be used:
post(io_context, handler);
binding an executor, e.g. using bind_executor
:
asio::post(bind_executor(ioc, handler));
E.g. here's my reviewed listing:
// #define _WIN32_WINNT 0x0A00
#include <iostream>
#include <boost/asio.hpp>
#include <sstream>
namespace asio = boost::asio;
inline static std::atomic_int tidgen = 0;
thread_local static int const thread_id = tidgen++;
static std::vector<std::string> parseLine(std::string input) {
std::vector<std::string> result;
std::istringstream iss(std::move(input));
for (std::string token; getline(iss, token, ';');)
result.push_back(token);
return result;
}
int main() try {
asio::io_context ioc;
for (std::string line; getline(std::cin, line);) {
for (std::string& token : parseLine(std::move(line))) {
auto handler = [t = std::move(token)] {
std::cout << "ThreadID = " << thread_id << "; " << t << std::endl;
};
asio::post(ioc, handler);
}
}
ioc.run();
} catch (std::exception const& exc) {
std::cerr << exc.what() << std::endl;
}
Compare with the version that accidentally uses system_executor