简体   繁体   中英

Qt 4.8 Signals/Slots not called after moveToThread()

I have a class derived from QObject , UploadWorker , that has been started using the recommended way of running tasks in threads as demonstrated in the Qt documentation.

QThread*      thread = new QThread();
UploadWorker* worker = new UploadWorker();

worker->moveToThread(thread);

connect(thread, SIGNAL(started()), worker, SLOT(doWork()));

Now, that works perfectly fine. My UploadWorker then tries to start a worker for himself, named Uploader , using the same technique.

Here are the necessary parts of the headers.

class UploadWorker : public QObject
{
    Q_OBJECT
public:
    // stuff

public slots:
    void doWork();

signals:
    void allWorkDone();

protected:
    void startUploader();
};

class Uploader : public QObject
{
    Q_OBJECT
public:
    // stuff

public slots:
    void doWork();
    void finishWhenQueueIsEmpty();
}; 

This is the implementation of UploadWorker .

void
UploadWorker::doWork()
{
    // This method is called when QThread emits started()

    // Prepare the upload thread
    startUploader();

    // Do some important work...

    // Notify the upload thread that we're done
    emit allWorkDone();
}

void
UploadWorker::startUploader()
{
    QThread*  thread = new QThread();
    Uploader* uploader = new Uploader();

    uploader->moveToThread(thread);

    connect(this,     SIGNAL(stopped()),   uploader, SLOT(stop()));
    connect(uploader, SIGNAL(finished()),  thread,   SLOT(quit()));
    connect(thread,   SIGNAL(finished()),  thread,   SLOT(deleteLater()));
    connect(uploader, SIGNAL(finished()),  uploader, SLOT(deleteLater()));
    connect(thread,   SIGNAL(started()),   uploader, SLOT(doWork()));
    connect(this,     SIGNAL(allWorkDone()), 
            uploader, SLOT(finishWhenQueueIsEmpty()));

    thread->start();
}

My problem now is, that the Uploader::finishWhenQueueIsEmpty() slot is never triggered. Unless: I remove uploader->moveToThread(thread); .

Where's the bug?

edit I forgot to mention that the Uploader::doWork() method is called when the thread starts. It's any other slot, in this example finishWhenQueueIsEmpty() that don't work.

I fixed a typo regarding the name of the slots.

One very probable possibility is that you're executing your Uploader::doWork() slot and wait for a call to the slot Uploader::finishWhenQueueIsEmpty() to finish its execution, through some synchronization mechanism (a boolean isRunning for example).

What you need to do then is connecting your slot like the following:

connect(this,     SIGNAL(allWorkDone()), 
        uploader, SLOT(finishWhenQueueIsEmpty()),
        Qt::DirectConnection);

and in finishWhenQueueIsEmpty() protect the synchro mechanism with a mutex.

Why?

Because the default mode for connection that are made between objects that are in different thread is Qt::QueuedConnection . What it does here is that every slot of the Uploader class here are queued for execution, that is, the next slot in the queue will run once the thread returns to the event loop (ie once the current slot has finished running). Then, it means that in this mode, calling Uploader::doWork() and then Uploader::finishWhenQueueIsEmpty() will result in the program being stuck in doWork() .

With the Qt::DirectConnection , the slot will be run in the thread that runs UploaderWorker::doWork() and can actually be run while Uploader::doWork() is running. But then you have two threads that can access the same Uploader object and thus you need to protect every access/write to parts of this object.

void
UploadWorker::doWork()
{
    // This method is called when QThread emits started()

    // Prepare the upload thread
    startUploader();

    // Do some important work...

    // Notify the upload thread that we're done
    emit allWorkDone();
}

the structure here is telling me that you don't let the event loop run until you are actually done with the "important work", this means that no slots will be triggered.

either let the events run by calling QApplication::processEvents(); periodically or redesign that work so it is processed by a sequence of calls from the event loop.

In the Uploader class you label the slot: void finishedWhenQueueIsEmpty();

Then you call:-

connect(this, SIGNAL(allWorkDone()), uploader, SLOT(finishWhenQueueIsEmpty()));

So one is 'finishWhen...' and the other 'finishedWhen..'

If you use this connect syntax (pre Qt 5.0) then you can either check the return value from the connect call, or at least look at the debug output when running in a debugger, which will have told you that the slot was invalid.

With Qt 5 connect syntax, this can be caught at compile time:-

connect(this, &UploadWorker::allWorkDone() uploader, &Uploader::finishWhenQueueIsEmpty());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM