It might help to think of a single io_service *instance* as a single event loop. When you invoke run() on instance X of an io_service from thread Y, then X is allowed to post notifications to thread Y when an event happens on that io_service. So, for example, if you have one io_service and you invoke run() from 5 threads on the same instance, then the all 5 threads will be participating in the event loop for this instance of io_service. It is undefined which thread will receive notification of an event, only that exactly 1 of the threads in the event loop will wake up when something happens. So if you have 2 io_service instances, then the output can be interleaved if you call run from multiple threads. In your program, you declare 2 io_services, but you actually only use one. Maybe this is a typo? io2 isn't even used, so io2.run() just returns instantly. I also rewrote your code a little. It helps to print out the time until expiration for each timer. boost::asio::io_service io,io2; boost::asio::deadline_timer* timer1; boost::asio::deadline_timer* timer2; void print(const boost::system::error_code& /*e*/, boost::asio::deadline_timer* t, int* count) { if (*count < 5) { ++(*count); t->expires_at(t->expires_at() + boost::posix_time::seconds(1)); std::cout << *count << ": " << ((t==timer1)?"timer1":"timer2") << ", t1exp: " << timer1->expires_from_now() << ", t2exp: " << timer2->expires_from_now() << std::endl; t->async_wait(boost::bind(print, boost::asio::placeholders::error, t, count)); } } int main() { int count = 0; timer1 = new boost::asio::deadline_timer(io, boost::posix_time::seconds(1)); timer2 = new boost::asio::deadline_timer(io, boost::posix_time::seconds(1)); std::cout << "[start]: t1exp: " << timer1->expires_from_now() << ", t2exp: " << timer2->expires_from_now() << std::endl; timer1->async_wait(boost::bind(print, boost::asio::placeholders::error, timer1, &count)); timer1->expires_at(timer1->expires_at() + boost::posix_time::seconds(1)); timer2->async_wait(boost::bind(print, boost::asio::placeholders::error, timer2, &count)); io.run(); io2.run(); std::cout << "Final count is " << count << "\n"; int discard; std::cin>>discard; return 0; } [start]: t1exp: 00:00:01, t2exp: 00:00:01 1: timer1, t1exp: 00:00:03, t2exp: 00:00:01 2: timer2, t1exp: 00:00:02, t2exp: 00:00:01 3: timer2, t1exp: 00:00:01, t2exp: 00:00:01 4: timer2, t1exp: 00:00:00, t2exp: 00:00:01 5: timer1, t1exp: 00:00:01, t2exp: 00:00:01 Final count is 5 Does this make it clearer why it's behaving the way it is?