Why are emitted signals not received in multithreading? - c++

I have a problem, that remaining sent signals are not received after calling quit on QThread object.
The scenario contains 2 additional threads (QThread and std::thread) and the main execution thread. Let's call the QThread Q, the std::thread T and the main thread M.
In M I create Q, the Receiver-object R "living" in Q and the Sender-object S. Also a std::thread T is created executing a bunch if emits with S.
class Sender : public QObject
{
Q_OBJECT;
public:
std::vector<int> m_Sent;
Sender()
{
}
public slots:
signals:
void signal(int i);
public:
void send(int i)
{
m_Sent.emplace_back(i);
emit signal(i);
}
};
class Receiver : public QObject
{
Q_OBJECT;
public:
std::vector<int> m_Received;
Receiver()
{
}
void Connect(Sender* s)
{
connect(s, &Sender::signal, this, &Receiver::slot, Qt::QueuedConnection);
}
void Disconnect(Sender* s)
{
disconnect(s, &Sender::signal, this, &Receiver::slot);
}
public slots:
void slot(int i)
{
m_Received.emplace_back(i);
}
};
void main(int argc, char** argv)
{
QApplication app(argc, argv);
qint64 random_seed = QDateTime::currentMSecsSinceEpoch();
std::cout << "Setting random seed " << random_seed << "\n";
std::srand(random_seed);
std::unique_ptr<Receiver> R(new Receiver);
std::unique_ptr<Sender> S(new Sender);
auto actions = [&S]() {
int i = 0;
std::chrono::steady_clock::time_point current =
std::chrono::steady_clock::now();
std::chrono::steady_clock::time_point finish =
current + std::chrono::milliseconds(100);
while (current < finish)
{
std::this_thread::sleep_for(std::chrono::microseconds(std::rand()%1000));
S->send(i++);
std::this_thread::sleep_for(std::chrono::microseconds(std::rand()%1000));
S->send(i++);
std::this_thread::sleep_for(std::chrono::microseconds(std::rand()%1000));
S->send(i++);
std::this_thread::sleep_for(std::chrono::microseconds(std::rand()%1000));
S->send(i++);
std::this_thread::sleep_until(current + std::chrono::milliseconds(17));
current = std::chrono::steady_clock::now();
}
};
std::unique_ptr<QThread> Q(new QThread());
R->moveToThread(Q.get());
R->Connect(S.get());
Q->start();
std::thread T(actions);
T.join();
// approach 1:
QMetaObject::invokeMethod(Q.get(), "quit", Qt::QueuedConnection);
Q->wait(); // never returns
// approach 2:
Q->quit();
Q->wait(); // missing events
std::cout << "Sent: ";
for(auto v : S->m_Sent)
{
std::cout << v << " ";
}
std::cout << std::endl;
std::cout << "Received: ";
for(auto v : R->m_Received)
{
std::cout << v << " ";
}
std::cout << std::endl;
}
I'm working on Windows with VS2013 and Qt 5.5.1. I tested it with kind of counter in R to track received signals. While debugging I went through all emits so all should be inserted to event loop in Q. After Q.wait() the counter for the slots do not correspond to the emitted signals. I would have expected the event loop with remaining input events was handled by Q.quit() or Q.wait() but seems not so, It's always that there is a cut of "event-stream" from a certain point onward. I tried now for 4 days going through Qt-Docu and several other stuff found by google, but no proposal worked so far.

I am not 100% sure since the documentation is not crystal clear, but what makes you think that the even loop is processing all pending events before exiting ? My assumption would be that there is a check "should I exit" at every loop and that it can discard some pending events when the exit flag is set.
In order to summarize the discussion below, I would suggest to add a new signal that you emit from wherever you want (say for instance from the std::thread once you have emitted everything you wanted) that would get into the QThread event loop queue and be connected to the QThread quit method so that the thread exits when being processed.
You can also avoid defining a new signal if you want.
Your code would look like (not tested):
Sender S = new Sender();
QThread Q = new QThread();
Receiver R = new Receiver();
R->moveToThread(Q);
connect(S, &Sender::signal, R, &Receiver::slot, Qt::QueuedConnection);
Q->start();
while(!Q.isRunning())
{
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
std::thread T([&S](){
emit S->signal(); // only an example, several other connects are used too
})
T.join();
QMetaObject::invokeMethod(Q, "quit",
Qt::QueuedConnection);
Q.wait();

Related

How to use boost::asio::deadline_timer with Qt?

How could using boost::asio::deadline_timer with Qt ? I am new to Boost C++ library and try documentation examples in separate project and it work. But i could not using it in my project which use Qt for GUI.
Example of my problem
Note:
I Need a long interval and QTimer could not work with a long interval.
Based on an answer to your previous question it's very easy to implement your own deadline timer using Qt only (that seems to be what you'd prefer)...
class deadline_timer: public QObject {
using super = QObject;
Q_OBJECT;
public:
using deadline_id = uint_least64_t;
signals:
void timeout(deadline_id, QDateTime) const;
public:
explicit deadline_timer (int resolution_milliseconds = 1000, QObject *parent = nullptr)
: super(parent)
{
m_timer.setInterval(resolution_milliseconds);
QObject::connect(&m_timer, &QTimer::timeout, this, &deadline_timer::handle_timeout);
}
/*
* Adds a new deadline and returns associated id. When the deadline is
* reached the timeout signal will be emitted with the corresponding id
* and time.
*/
deadline_id start (const QDateTime &deadline)
{
m_deadlines[deadline].insert(++s_id);
if (!m_timer.isActive()) {
m_timer.start();
}
return s_id;
}
void stop ()
{
m_timer.stop();
}
private:
void handle_timeout ()
{
auto now = QDateTime::currentDateTime();
for (auto i = m_deadlines.begin(); i != m_deadlines.end() && now >= i->first; i = m_deadlines.begin()) {
for (const auto &j: i->second) {
emit timeout(j, i->first);
}
m_deadlines.erase(i);
}
if (m_deadlines.empty()) {
m_timer.stop();
}
}
static deadline_id s_id;
QTimer m_timer;
std::map<QDateTime, std::set<deadline_id>> m_deadlines;
};
deadline_timer::deadline_id deadline_timer::s_id = 0;
The use as...
deadline_timer dt;
QObject::connect(&dt, &deadline_timer::timeout,
[](deadline_timer::deadline_id id, QDateTime deadline)
{
std::cout << "\ndeadline " << id << " passed\n";
});
auto id = dt.start(QDateTime::currentDateTime().addYears(1));

Receiving LCM messages via signals/slots in C++ with Qt?

In my application, I am receiving messages from LCM (Lightweight Communications and Marshalling) that contain data for multiple consumers within the application. I imagined this working with the LCM handler as a singleton so that there is one instance that each class could use. For example, each consumer class would have:
QObject::connect(LCMHandler::getInstance(), SIGNAL(messageReceived()),
this, SLOT(processMessage()));
Where lcmhandler.h is:
class LCMHandler : public QObject
{
Q_OBJECT
public:
static LCMHandler* getInstance();
LCMHandler();
~LCMHandler() {}
void handleMessage(const lcm::ReceiveBuffer* rbuf,
const std::string &chan,
const example::example_t *msg);
signals:
void messageReceived();
private:
static LCMReceiver* _instance;
};
And lcmhandler.cpp is:
LCMHandler* LCMHandler::_instance = 0;
LCMHandler::LCMHandler()
{
lcm::LCM lcm;
if(lcm.good())
{
lcm.subscribe("MyChannel", &LCMHandler::handleMessage, this);
while(0 == lcm.handle());
} else {
std::cerr << "LCM Error" << std::endl;
}
}
LCMHandler* LCMHandler::getInstance() {
if (!_instance) {
_instance = new LCMHandler();
}
return _instance;
}
void LCMHandler::handleMessage(const lcm::ReceiveBuffer *rbuf,
const std::string &chan,
const hlelcm::transponder_t *msg)
{
std::cout << "Received message on channel " << chan.c_str() << std::endl;
emit messageReceived();
}
The application successfully prints "Received message on channel..." repeatedly; however, nothing else is executed, including code in the consumer class's processMessage(), presumably because the application gets stuck looping on handleMessage(...) and never executes the signal/slot procedure (or refreshes the UI components). So, if the implementation of processMessage() is:
void Consumer::processMessage() {
std::cout << "Message received" << std::endl;
}
It never executes, while handleMessage(...) loops infinitely. Similarly, the Qt UI never loads because handleMessage is busy looping.
What is the best way to handle the incoming messages? Should I refrain from using a singleton for LCMHandler? What do I need to change to make this implementation work?
Move the contents of your LCM constructor to another function:
LCMHandler::beginCommunication()
{
lcm::LCM lcm;
if(lcm.good())
{
//QObject base class call.
moveToThread( &_myLocalQThread );
_myLocalThread.start();
lcm.subscribe("MyChannel", &LCMHandler::handleMessage, this);
_isActive = true;
// This is blocking, we don't want it to interfere with
// the QApplication loop
while(0 == lcm.handle());
}
else
{
std::cerr << "LCM Error" << std::endl;
}
_isActive = false;
}
Then something along these lines to allow your LCM loop to happen in another thread.
auto lcmHandler = LCMHandler::getInstance();
// I like to be explicit about the Qt::QueuedConnection. Default behavior should be thread safe, though.
connect( lcmHandler, &LCMHandler::messageReceived,
this, &Consumer::processMessage, Qt::QueuedConnection );
// Add a std::atomic< bool > _isActive to LCMHandler
if( not lcmHandler.isActive() )
{
lcmHandler.beginCommunication();
}
And then make sure to properly close your QThread in the destructor.
LCMHandler::~LCMHandler()
{
_myLocalQThread.quit();
_myLocalQThread.wait();
}

print trace of signals/slots called

Is there a way to print the signals and slots being called?
I'm experiencing a weird deadlock in Qt which happens only in particular conditions across multiple threads, and I'd like to know the order of signals/slots being called.
Of course, for slots, I write the method body, and in the worst scenario, I can manually add a print out of the method. But the body of signals is generated automatically, so it is not possible, unless I write a custom moc, which seems like an overkill for this task...
If one leverages the built-in hooks, it's possible to automatically instrument all signals, and all slots connected using the Qt 4 connect syntax. Unfortunately, QtPrivate::QSlotObject doesn't implement these hooks: slots connected using the Qt 5 syntax need to be instrumented manually (e.g. by connecting a functor to them, or adding code to them).
Signal notifications can be relied on for connected signals. Objects with no signals, and some signals of objects with other connections, will not be reported. This is presumably what you want.
Thus:
// https://github.com/KubaO/stackoverflown/tree/master/questions/signal-spy-39597233
#include <QtCore>
#include <private/qobject_p.h>
int signalToMethodIndex(const QMetaObject * mo, int signal)
{
Q_ASSERT(signal >= 0);
for (int i = 0; i < mo->methodCount(); ++i) {
if (mo->method(i).methodType() == QMetaMethod::Signal) {
if (signal == 0) return i;
-- signal;
}
}
return -1;
}
class Spy {
static QThreadStorage<bool> entered;
static void signalBegin(QObject *caller, int signalIndex, void **) {
if (entered.localData()) return;
QScopedValueRollback<bool> roll{entered.localData(), true};
auto index = signalToMethodIndex(caller->metaObject(), signalIndex);
if (index >= 0)
qDebug() << "SIGNAL" << caller << caller->metaObject()->method(index).methodSignature();
}
static void slotBegin(QObject *caller, int index, void **) {
if (entered.localData()) return;
QScopedValueRollback<bool> roll{entered.localData(), true};
qDebug() << "SLOT" << caller << caller->metaObject()->method(index).methodSignature();
}
public:
static void start() {
QSignalSpyCallbackSet set{&signalBegin, &slotBegin, nullptr, nullptr};
qt_signal_spy_callback_set = set;
}
};
QThreadStorage<bool> Spy::entered;
struct Class : QObject {
Q_SIGNAL void aSignal();
Q_SLOT void aSlot() { qDebug() << "slot"; }
Q_OBJECT
};
int main(int argc, char ** argv) {
Spy::start();
QCoreApplication app{argc, argv};
Class obj;
QObject::connect(&obj, SIGNAL(aSignal()), &obj, SLOT(aSlot()));
obj.setObjectName("obj");
emit obj.aSignal();
}
#include "main.moc"
Output:
SIGNAL Class(0x7fff51901af0, name = "obj") "objectNameChanged(QString)"
SIGNAL Class(0x7fff51901af0, name = "obj") "aSignal()"
SLOT Class(0x7fff51901af0, name = "obj") "aSlot()"
slot

Boost Timer in Qt Application

I'm trying to create qt application that uses a timer from boost,
I have a problem with the following code
Compiled on osx, qt 5.2, boost 1.55
This is my code:
CCTimer::CCTimer(int interval)
: m_pingTimer(m_ioServiceTimer, boost::posix_time::seconds(interval)), m_interval(interval)
{
m_isRunning = false;
}
CCTimer::~CCTimer()
{
Stop();
}
void CCTimer::Start()
{
if(!m_isRunning)
{
m_isRunning = true;
Reset(m_interval);
m_timerThread = boost::shared_ptr<boost::thread>(new boost::thread(boost::bind(&boost::asio::io_service::run, &m_ioServiceTimer)));
}
}
void CCTimer::Reset(int durationTime)
{
m_beforeTime = boost::posix_time::microsec_clock::universal_time();
m_pingTimer.expires_from_now(boost::posix_time::seconds(durationTime));
m_pingTimer.async_wait(boost::bind(&CCTimer::Wait, this, _1));
}
void CCTimer::Wait(const boost::system::error_code& errorCode)
{
boost::posix_time::time_duration duration = boost::posix_time::microsec_clock::universal_time() - m_beforeTime;
std::cout << boost::posix_time::microsec_clock::universal_time() << std::endl;
if(duration.seconds() > m_interval) {
std::cout << "Error time " << std::endl;
}
Reset(m_interval);
}
void CCTimer::Stop()
{
if(m_isRunning)
{
m_isRunning = false;
m_pingTimer.cancel();
m_ioServiceTimer.stop();
m_timerThread->join();
m_ioServiceTimer.reset();
}
}
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
CCTimer timer(1);
timer.Start();
return app.exec();
}
After creation of the timer in qt application, console shows:
2014-Mar-26 22:04:30.549722
2014-Mar-26 22:04:31.550977
2014-Mar-26 22:04:32.552229
2014-Mar-26 22:04:33.553467
2014-Mar-26 22:04:34.554734
2014-Mar-26 22:04:43.684300
Error time
2014-Mar-26 22:04:54.694440
Error time
2014-Mar-26 22:05:05.694371
Error time
2014-Mar-26 22:05:11.669329
Error time
what can be wrong ?
I haven't spotted anything glaring. Though, of course, you never mention whether/when/where you class Stop() and Start().
I made the implementation marginally simpler and fixed a few potential races:
no useless use of shared pointer for the time thread
make the CCTimer non-copyable
no more m_isRunning kludge; instead, intentionally cancel the timer and detect cancellation so you can end the thread. If the thread is joinable(), it's running
fixed a race condition where you cancel the timer from the thread that calls Stop(), whereas the timer is being async-awaited by the io_service thread, byt posting the cancel to the io_service thread too:
void Stop()
{
if(m_thread.joinable())
{
m_service.post(boost::bind(&deadline_timer::cancel, &m_timer));
m_thread.join();
m_service.stop();
m_service.reset();
}
}
fixed a cosmetic thing that could have led to confusion where you use a different timestamp for printing than you do for calculating the elapsed time
Update
So I downloaded Qt5 and goaded my compiler to use it. /GREAT SUCCESS/. And indeed, I got the same behaviour (but far worse, sometimes). The strange thing was it blocked more on the terminal than the eventual output would show. This made me suspect it's maybe Qt that's intercepting/reopening the stdin/stdout streams for it's own purposes and adding some blocking/buffering behaviour in the process.
I checked this by writing the timer trace to a separate file instead, and indeed this removed the symptoms (when left running for over hald an hour). So this is likely the cause of your issue. Just don't write to stdout from your timer thread and your spurious blocking issue should be gone.
Here's the code I used to test this, with the above suggestions incorporated:
#include <boost/date_time/posix_time/posix_time_io.hpp>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/shared_ptr.hpp>
#include <QtGui/QGuiApplication>
#include <QtCore/QCoreApplication>
#include <fstream>
using namespace boost::asio;
namespace pt = boost::posix_time;
class CCTimer : boost::noncopyable
{
io_service m_service;
deadline_timer m_timer;
pt::ptime m_starttime;
int m_interval_seconds;
boost::thread m_thread;
std::ofstream ofs;
public:
CCTimer(int interval)
: m_service(),
m_timer(m_service),
m_interval_seconds(interval)
{
assert(m_interval_seconds>0);
}
~CCTimer()
{
Stop();
}
void Start()
{
if(!m_thread.joinable())
{
ofs.open("output.log");
Reset(m_interval_seconds);
m_thread = boost::thread(boost::bind(&io_service::run, &m_service));
}
}
void Stop()
{
if(m_thread.joinable())
{
m_service.post(boost::bind(&deadline_timer::cancel, &m_timer));
m_thread.join();
ofs.close();
m_service.stop();
m_service.reset();
}
}
private:
static pt::ptime now() { return pt::microsec_clock::universal_time(); }
void Reset(int durationTime)
{
m_timer.expires_from_now(pt::seconds(durationTime));
m_starttime = now();
m_timer.async_wait(boost::bind(&CCTimer::Elapsed, this, placeholders::error));
}
void Elapsed(const boost::system::error_code& errorCode)
{
pt::ptime const event = now();
pt::time_duration const elapsed = event - m_starttime;
if (errorCode != error::operation_aborted && ofs.is_open() && ofs.good())
{
ofs << event << " (" << elapsed.total_milliseconds() << "ms) ";
if(elapsed.seconds() > m_interval_seconds) {
ofs << " Overdue!" << std::endl;
} else {
ofs << "" << std::endl;
}
Reset(m_interval_seconds);
} else
{
ofs << "Stopped (" << elapsed.total_milliseconds() << "ms)" << std::endl;
}
}
};
int main(int argc, char** argv)
{
QGuiApplication app(argc, argv);
CCTimer timer(1);
timer.Start();
return app.exec();
}
Old suggestions:
I'd have to assume you're doing something in the rest of the code that inhibits the CCTimer thread
are you running under a debugger, profiler, whatnot?
are you running under a (overwhelmed) virtualizer?
are you doing more on the same io_service/thread?
can IO be blocking? In particular do you log the timestamps to an actual console or to a file/pipe? Could the output pipe be blocking the timer thread?

deadline_timer strange behavior

I have a test class like this. What I want to do is to keep running the three timers in this object. But after I instantiate an object, some timer just keeps repeating but others will disappear after like 3 mins. Can anyone explain this for me?
class EventProcessor
{
private:
boost::asio::deadline_timer* m_Timer0;
boost::asio::deadline_timer* m_Timer1;
boost::asio::deadline_timer* m_Timer2;
boost::asio::io_service io0;
boost::asio::io_service io1;
boost::asio::io_service io2;
int TimerInterval[3];
boost::asio::deadline_timer* Timers[3];
public:
EventProcessor(int p_0, int p_1, int p_2)
{
TimerInterval[0] = p_0;
TimerInterval[1] = p_1;
TimerInterval[2] = p_2;
m_Timer0= new boost::asio::deadline_timer(io0, boost::posix_time::seconds(TimerInterval[0]));
Timers[0] = m_Timer0;
m_Timer1 = new boost::asio::deadline_timer(io1, boost::posix_time::seconds(TimerInterval[1]));
Timers[1] = m_Timer1;
m_Timer2 = new boost::asio::deadline_timer(io2, boost::posix_time::seconds(TimerInterval[2]));
Timers[2] = m_Timer2;
m_Timer0->async_wait(boost::bind(&EventProcessor::HandleExpire, this, boost::asio::placeholders::error, 0));
m_Timer1->async_wait(boost::bind(&EventProcessor::HandleExpire, this, boost::asio::placeholders::error, 1));
m_Timer2->async_wait(boost::bind(&EventProcessor::HandleExpire, this, boost::asio::placeholders::error, 2));
StartWithNewThread(0);
StartWithNewThread(1);
StartWithNewThread(2);
}
private:
void HandleExpire(const boost::system::error_code& p_ec, int p_TimerIndex)
{
if(p_ec == boost::asio::error::operation_aborted)
{
std::cout << "Timer" << p_TimerIndex << " canceled" << std::endl;
return;
}
std::cout << "Timer" << p_TimerIndex << " expired" << std::endl;
//Reset(p_OriginalTimer, TimerInterval[p_TimerIndex], p_TimerIndex);
boost::thread Thread(boost::bind(&EventProcessor::Reset, this, p_TimerIndex, TimerInterval[p_TimerIndex]));
}
void Start(int p_Index)
{
boost::asio::io_service& UnderlyingIO = Timers[p_Index]->get_io_service();
UnderlyingIO.reset();
UnderlyingIO.run();
UnderlyingIO.stop();
return;
}
void StartWithNewThread(int p_Index)
{
boost::thread Thread(boost::bind(&EventProcessor::Start, this, p_Index));
std::cout << Thread.get_id() << "<->" << "Timer" << p_Index << std::endl;
return;
}
public:
void Reset(int p_Index, int p_Seconds)
{
Timers[p_Index]->cancel();
Timers[p_Index]->expires_from_now(boost::posix_time::time_duration(0,0,p_Seconds,0));
TimerInterval[p_Index] = p_Seconds;
Timers[p_Index]->async_wait(boost::bind(&EventProcessor::HandleExpire, this, boost::asio::placeholders::error, p_Index));
boost::asio::io_service& UnderlyingIO = Timers[p_Index]->get_io_service();
UnderlyingIO.reset();
UnderlyingIO.run();
UnderlyingIO.stop();
return;
}
};
So this is how you should do it:
#include "test.h"
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/atomic.hpp>
class EventProcessor
{
private:
std::unique_ptr<boost::asio::deadline_timer> m_Timers[3];
boost::asio::io_service service;
boost::atomic<int> TimerInterval[3];
public:
EventProcessor(int time0,int time1, int time2)
{
TimerInterval[0] = time0;
TimerInterval[1] = time1;
TimerInterval[2] = time2;
for (int i = 0; i < 3; i++)
{
m_Timers[i].reset(
new boost::asio::deadline_timer(service));
}
}
~EventProcessor()
{
service.stop();
for (int i = 0; i < 3; i++)
{
m_Timers[i]->cancel();
}
}
void Run()
{
for (int i = 0; i < 3; i++)
{
m_Timers[i]->expires_from_now(boost::posix_time::seconds(TimerInterval[i]));
m_Timers[i]->async_wait(boost::bind(&EventProcessor::HandleExpire,
this,
i,
_1));
}
service.run();
}
void RunAsync()
{
boost::thread(boost::bind(&EventProcessor::Run,this));
}
void Reset(int i,int seconds)
{
TimerInterval[i] = seconds;
m_Timers[i]->expires_from_now(boost::posix_time::seconds(TimerInterval[i]));
m_Timers[i]->async_wait(boost::bind(&EventProcessor::HandleExpire,
this,
i,
_1));
}
private:
void HandleExpire(int p_TimerIndex, const boost::system::error_code& error)
{
if(error == boost::asio::error::operation_aborted)
{
std::cout << "Timer" << p_TimerIndex << " canceled" << std::endl;
return;
}
std::cout << "Timer" << p_TimerIndex << " expired" << std::endl;
//Reset(p_OriginalTimer, TimerInterval[p_TimerIndex], p_TimerIndex);
m_Timers[p_TimerIndex]->expires_from_now(
boost::posix_time::seconds(TimerInterval[p_TimerIndex]));
m_Timers[p_TimerIndex]->async_wait(boost::bind(&EventProcessor::HandleExpire,
this,
p_TimerIndex,
_1));
}
};
int main()
{
EventProcessor ev(1,2,3);
ev.RunAsync();
getchar();
ev.Reset(2,4);
getchar();
}
Granted I don't have any of the fancy checkers to see if you are currently running or not (which you totally need if you want this to be safe to use).
You can think of boost::asio::io_service as a context in which async calls can be made. It creates a FIFO queue of messages to process, and processes them where and when you tell it to. The most common way to process these messages is boost::asio::io_service::run, which will process messages until there is nothing left to be done. "nothing left to be done" is a flexible definition: it doesn't necessarily mean there is a message to process, just that there is stuff to be done. Things like a deadline timer make sure that there is "something to be done" as long as an async_wait is going on until the handler is called. You can manually enforce that there is something to be done by creating a boost::asio::io_service::work instance. This makes it so that there is "something left to be done" for the lifetime of the work object.
The deadline timer class takes care of all the async calls for you, so you don't have to spawn all those threads. The io_service performs synchronization, which is necessary to prevent annoying control issues.
So to the problem with your code:
With all those threads controlling the io_service, it is hard to tell what is actually going wrong...I have to guess on what could possibly going wrong. I'd put my money on somewhere along the line, you call a io_service::cancel before a deadline timer times out, which will stop your loop. I solve this in my code by doing all the control (calling wait_async) in one synchronous thread (the io_service::run call) and only calling io_service::cancel when I want the code to stop.