Reputation: 17713
https://youtu.be/rwOv_tw2eA4?t=1030
This example has one io_service and two threads running on it.
the io_service has two tasks attached to it: timer1 and timer2
two threads are created to run the io_service
void timer_expired( std:: string id )
{
std::cout << timestamp() << ": " << id << ": begin\n";
std::this_thread::sleep_for( std::chrono::seconds(3) );
std::cout << timestamp() << ": " << id << ": end\n";
}
int main()
{
boost::asio::io_service io_service;
boost::asio::deadline_timer timer1( io_service, boost::posix_time::seconds(5) );
boost::asio::deadline_timer timer2( io_service, boost::posix_time::seconds(5) );
timer1.async_wait( []( auto ... ){ timer_expired("timer1"); });
timer2.async_wait( []( auto ... ){ timer_expired("timer2"); });
std::cout << timestamp() << ": calling io_service run\n";
std::thread thread1( [&](){ io_service.run(); } );
std::thread thread2( [&](){ io_service.run(); } );
thread1.join();
thread2.join();
std::cout << timestamp() << ": done\n";
return 0;
}
Every time I run this sample the output looks ok, in that:
the two timers started at the same time
the two timers expired at the same time (after 5s, they were async)
the callbacks were invoked at the same time (after 3s)
The author stated that this code has race in there andshould not work (garage output).
What's not very clear is that we have two threads, each can serve one completion handler (here the timer callback). So why race? And the fact that I ran this code several times and unable to produce any garbage output as what the author presented.
The output looks as expected, here's a sample:
2019-07-28 11:27:44: calling io_service run
2019-07-28 11:27:49: timer1: begin
2019-07-28 11:27:49: timer2: begin
2019-07-28 11:27:52: timer1: end
2019-07-28 11:27:52: timer2: end
2019-07-28 11:27:52: done
Upvotes: 1
Views: 83
Reputation: 20969
Handlers are invoked within io_service::run
. You started two threads where io.run()
works. So you have two runnig methods timer_expired
at the same time. And the race is while accessing cout
stream.
You are just lucky to see this nice output, but when you add some more works in timer_expired
:
void timer_expired( std:: string id )
{
std::cout << timestamp() << ": " << id << ": begin\n";
std::this_thread::sleep_for( std::chrono::seconds(3) );
// ADD MORE LINES TO BE PRINTED
for (int i = 0; i < 1000; ++i)
std::cout << timestamp() << ": " << id << ": end" << std::endl;
}
you will see interleaved characters.
Accessing by many threads cout
object doesn't lead to crash (according to reference),
Concurrent access to a synchronized ([ios.members.static]) standard iostream object's formatted and unformatted input and output functions or a standard C stream by multiple threads shall not result in a data race. [ Note: Users must still synchronize concurrent use of these objects and streams by multiple threads if they wish to avoid interleaved characters.
but to avoid these interleaved chars you have to add synchronization when accessing cout
for example by using std::mutex
, or call handlers in serially manner - use strand
object from boost.
Upvotes: 1