I am new to Boost::asio
and I am currently looking at io_context
.
In the docs https://www.boost.org/doc/libs/1_75_0/doc/html/boost_asio/reference/io_context.html shown is the following example:
{
...
}
...
boost::asio::io_context io_context;
// Submit a function to the io_context.
boost::asio::post(io_context, my_task);
// Submit a lambda object to the io_context.
boost::asio::post(io_context,
[]()
{
...
});
// Run the io_context until it runs out of work.
io_context.run();
However, I would like to be able to post
even after io_context.run()
has been called.
Essentially, something like this:
#include <boost/asio.hpp>
int value = -1;
void my_task()
{
value = 42;
}
int main() {
boost::asio::io_context io_context;
// Submit a function to the io_context.
//boost::asio::post(io_context, my_task);
// Run the io_context until it runs out of work.
io_context.run();
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> work(io_context.get_executor());
// Submit a lambda object to the io_context.
boost::asio::post(io_context,
[]()
{
my_task();
});
assert(value == 42);
}
After compiling the above with g -o a example.cpp -lboost_system -lpthread
I am getting an assertion failure. What is the "right" way to accomplish this?
CodePudding user response:
io_context::run()
blocks as long as there is work to do. In your first example, you could just wait until both the post
ed tasks are finished. After that, io_context::run()
returns and you can submit new work and call run()
again. If you don't want this sequential behavior but rather submit tasks while the io_context
works on tasks, you need a second thread, that executes io_conext::run()
. You can create one for example like this:
#include <thread>
//...
std::thread my_thread( [&](){ io_context.run(); } );
// post more work here
// wait for it to finish
my_thread.join();
Make sure that the io_context
object lives longer than the thread, if you use the lamda capture by reference like in this example.
CodePudding user response:
Either run the io_context on a separate thread, or, indeed use the execution context that already has that built in.
Using a manual thread
#include <boost/asio.hpp>
#include <iostream>
int value = -1;
void my_task() { value = 42; }
int main() {
boost::asio::io_context io_context;
auto work = make_work_guard(io_context);
std::thread thread([&] { io_context.run(); });
// submit to the io_context
post(io_context, my_task);
work.reset();
thread.join();
std::cout << value << "\n";
}
Prints
g -std=c 20 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
42
Using asio::thread_pool
:
#include <boost/asio.hpp>
#include <iostream>
int value = -1;
void my_task() { value = 42; }
int main() {
boost::asio::thread_pool io(1);
post(io, my_task);
io.join();
std::cout << value << "\n";
}
As you can see it's basically the same, but
- less code
- less
work
(literally) - more correct (see e.g. Should the exception thrown by boost::asio::io_service::run() be caught?)
- trivial to make multi-threaded
thread_pool io(16);
or just using the defaultthread_pool io;