Russian Qt Forum

Qt => Многопоточное программирование, процессы => Тема начата: nvek от Август 24, 2017, 12:48



Название: Как завершить поток таймера?
Отправлено: nvek от Август 24, 2017, 12:48
Опять я, опять, поток, опять таймер. Не понимаю.

Код:
#include "Timer.h"

наследник от QThread
Timer::Timer(int interval) :
m_interval(interval),
m_startFlag(true)
{

}

Timer::~Timer()
{
//delete timer;
//quit();
}

void Timer::run()
{
QTimer timer;
connect(&timer, &QTimer::timeout, this, &Timer::sg_TimOut);
connect(this, SIGNAL(timerStarted(int)), &timer, SLOT(start(int)));

if (m_startFlag)
timer.start(m_interval);
exec();

//mit finished(this->thread());
}

void Timer::startTimer(int interval)
{
m_interval = interval;
m_startFlag = true;
emit timerStarted(interval);
}

void Timer::stop()
{
m_startFlag = false;
emit timerStoped();
}

void Timer::quit()
{
//stop();
//QThread::quit();

}

как завершить этот поток?
QThread: Destroyed while thread is still running
Код:
m_tmr = new Timer(1000); //класс поток для таймера
    m_tmr->start();
connect(m_tmr, &Timer::sg_TimOut, this, &MainForm::updateByTimer, Qt::DirecConnection);
connect(m_tmr, &QThread::finished, m_tmr, &Timer::deleteLater);
так как же его правильно останавливать?
надо вначале остановить поток, затем удалить таймер.



Название: Re: Как завершить поток таймера?
Отправлено: nvek от Август 24, 2017, 12:52
а что если это сделать по другому? может быть унаследовать его от обжекта и сделать просто movetothread()/
или попытаться использовать QtConcerent.
ведь метод который мене нужен чтобы выполнялся в потоке всего один.


Название: Re: Как завершить поток таймера?
Отправлено: demaker от Август 28, 2017, 23:17
А какая  задача стоит перед Вами?


Название: Re: Как завершить поток таймера?
Отправлено: Bepec от Август 29, 2017, 01:59
У вас несколько ошибок.
Во первых вы не понимаете сигнал-слотовой системы и способов её соединения.
Во вторых вы пытаетесь отделить (зачем - непонятно) таймер от рабочего потока.
В третьих всё общение с потоками должно быть на сигнал-слотах, для облегчения ваших же проблем.

PS в некоторые моменты я сам задаюсь вопросом - как же будет работать поток в таком вот случае и иногда не нахожу ответов :)


Название: Re: Как завершить поток таймера?
Отправлено: demaker от Август 29, 2017, 08:20
Думаю должно работать норм ::)
*.h
Код
C++ (Qt)
class Thread : public QThread
{
   Q_OBJECT
 
public:
    Thread(QObject*parent = 0);
    virtual ~Thread();
 
public slots:
    void timeOut();
 
protected:
    void run();
}
 

*.cpp
Код
C++ (Qt)
Thread::Thread(QObject*parent): QThread(parent)
{
}
 
Thread::~Thread()
{
}
 
void Thread::run()
{
    QTimer timer;
    // Qt::DirectConnection чтобы слот обрабатывался в контексте того потока из которого был выслан сигнал
    connect(&timer,SIGNAL(timeout()),this,SLOT(timeOut()),Qt::DirectConnection);
    timer.start(50);
    exec();
}
 
void Thread::timeOut()
{
// do something
}
 

создаем и запускаем поток
Код
C++ (Qt)
void Widget::createThread()
{
   if(thread == NULL){
       thread = new Thread(0);
       thread->start();
   }
}
 

завершение потока
Код
C++ (Qt)
 
void Widget::quitThread()
{
    if(thread != NULL){
        thread->quit();
        thread->wait();
        delete thread; //thread->deleteLater();
        thread = NULL;
   }
}
 
 


Название: Re: Как завершить поток таймера?
Отправлено: ssoft от Август 29, 2017, 13:58
Думаю должно работать норм ::)
Код
C++ (Qt)
void Thread::run()
{
   QTimer timer;
   // Qt::DirectConnection чтобы слот обрабатывался в контексте того потока из которого был выслан сигнал
   connect(&timer,SIGNAL(timeout()),this,SLOT(timeOut()),Qt::DirectConnection);
   timer.start(50);
   exec();
}
 

Здесь кроется серьезная проблема. В такой реализации доступ к методу timeOut() экземпляра типа Thread производится из потока, который с ним никак не связан (в контексте Qt).
То есть априорно формируются грабли с одновременным многопоточным вызовом timeOut.


Название: Re: Как завершить поток таймера?
Отправлено: Bepec от Август 29, 2017, 14:03
to ssoft: Ну, там надо в конструкторе moveToThread(this) дописать и тогда будет работать нормально :D

А так это извечная проблема потоков. Я кстати так до сих пор их и не осилил. К примеру, как правильно остановить событийный QThread ( где в run exec()).


Название: Re: Как завершить поток таймера?
Отправлено: demaker от Август 29, 2017, 15:12
to ssoft: Ну, там надо в конструкторе moveToThread(this) дописать и тогда будет работать нормально :D

А так это извечная проблема потоков. Я кстати так до сих пор их и не осилил. К примеру, как правильно остановить событийный QThread ( где в run exec()).
;D да согласен забыл написать ::)
Код:
moveToThread(this)


Название: Re: Как завершить поток таймера?
Отправлено: ssoft от Август 29, 2017, 15:42
to ssoft: Ну, там надо в конструкторе moveToThread(this) дописать и тогда будет работать нормально :D

Не будет). Если написать в конструкторе moveToThread(this), то либо проблема переносится в конструктор/деструктор, если экземпляр объекта создается в стеке; либо ваш экземпляр Thread зависнет в куче (memory leaks); либо придется писать какие-нибудь костыли (deleteLater() здесь не поможет).

А так это извечная проблема потоков. Я кстати так до сих пор их и не осилил. К примеру, как правильно остановить событийный QThread ( где в run exec()).

Да ничего в них проблемного нет). Все зависит от того, как нужно его остановить. Если хотите прервать - одно решение; если хотите завершить все текущие события в очереди - другое; если хотите завершить все события, пока очередь не опустеет (с учетом новых поступлений) - третье. По умолчанию Qt предоставляет метод завершения quit() со своей логикой.

Единственная особенность восприятия архитектуры Qt для работы с потоками связана с тем, что экземпляр типа QThread сам по себе организует только инфраструктуру для отдельного потока, но сам к нему не относится ни под каким соусом. То есть экземпляр типа QThread в рамках модели Qt связан с очередью того потока в котором он создан, а не с той, которую он сам организует. А метод moveToThread() - это еще тот костыль (причем встроенный в Qt), который следует использовать с большим количеством оговорок, ограничений и точным пониманием того, что вы делаете на свой страх и риск.


Название: Re: Как завершить поток таймера?
Отправлено: demaker от Август 29, 2017, 15:59
Ну тогда другой вариант.
Делать бесконечный цикл в run запускать счетчик и считать тики.
По дрстижению каког-то значения эмитить сигнал из потока.
Но опять же - будет ли это работать правильно и успевать отрабатывать.



Название: Re: Как завершить поток таймера?
Отправлено: Bepec от Август 29, 2017, 16:00
to ssoft- работа будет происходить в одном потоке. И всё будет хорошо. Абсолютно.

А по поводу moveToThread, ну... такой себе костыль, реализация которого равнозначна реализации потоков :D


Название: Re: Как завершить поток таймера?
Отправлено: ssoft от Август 30, 2017, 09:23
to ssoft- работа будет происходить в одном потоке. И всё будет хорошо. Абсолютно.

Предположим кто-то решил использовать такой класс

Пример 1. Размещение в стеке

Код
C++ (Qt)
void foo ()
{
   Thread thread;
   thread.moveToThread( &thread );
   thread.start();
}
 

Здесь реализуется непредсказуемое поведение. Как минимум получите предупреждение (QThread: Destroyed while thread is still running), как максимум крэш при обращении к атрибутам экземпляра thread.

Пример 2. Размещение в куче

Код
C++ (Qt)
void foo ()
{
   Thread * thread = new Thread;
   thread->moveToThread( thread );
   thread->start();
}
 

В этом случае удалять thread можно только после завершения потока, то есть, как минимум, нужно вызвать два метода quit и wait. Иначе возможны проблемы такие же как и в примере 1. Если не удалять thread, тогда буду утечки памяти (parent указать в этом случае нельзя). Нужно помнить, что к указателю thread теперь нельзя обращаться в этом потоке напрямую (или городить огород с мутексами и т.п.). Нюансов такого использования масса, я еще не все перечислил. Поэтому, если вы точно знаете что делаете, то флаг вам в руки. Но я как-то сомневаюсь, что новички это себе представляют.

По самой задаче: Как завершить поток таймера?

Если вы используете реализацию отсюда http://www.prog.org.ru/index.php?topic=31547.msg233225#msg233225 , то завершение потока таймера производится обычным способом завершения потока quit(), можно также подождать сам момент завершения с помощью wait().


Название: Re: Как завершить поток таймера?
Отправлено: Bepec от Август 30, 2017, 13:34
Пример 1 - самая распространённая ошибка, вы убиваете объект после выхода их функции. Что блин, похоже на ошибку первоклашки в С++. И никоим образом не относится к потоку. Любой класс работающий дольше 5 секунд будет выдавать те же ошибки и поведение :D

Пример 2 - всё правильно. Поток можно удалять ТОЛЬКО после завершения работы.

Ответ = вызов в нём deleteLater и quit.


Название: Re: Как завершить поток таймера?
Отправлено: Igors от Август 30, 2017, 14:10
Есть 2 хорошо известных сущности: timer и thread. Так за каким же <template> называть Timer'ом наследника QThread ??? Чтобы сразу капитально заморочить голову всем, и в первую очередь самому себе?

Чего Вы хотите добиться - я так и не понял. Запустить нитку в которой тикает таймер? QThread имеет методы startTimer и killTimer унаследованные от QObject, почему бы не использовать их? Как всегда. не забыть moveToThread(this)


Название: Re: Как завершить поток таймера?
Отправлено: ssoft от Август 30, 2017, 16:08
Ответ = вызов в нём deleteLater и quit.

Предположим, что deleteLater() для экземпляра thread отработал, это значит еще не был завершен его метод run() и exec() в другом потоке, что может быть чревато последствиями - QThread: Destroyed while thread is still running и все что описано ранее в примере 1.
Если же сначала завершить поток, а затем вызвать deleteLater(), то экземпляр thread не будет удален совсем - утечки памяти.

Простой пример

Код
C++ (Qt)
class Thread
: public QThread
{
public:
   ~Thread ()
   {
       ::std::cout << "Deleted" << ::std::endl;
   }
 
   virtual void run ()
   {
       ::std::cout << "Before exec" << ::std::endl;
       exec();
       ::std::cout << "After exec" << ::std::endl;
   }
};
 
int main ( int argc, char ** argv )
{
   QCoreApplication app( argc, argv );
 
   QThread * thread = new Thread;
   thread->moveToThread( thread );
   thread->start();
   thread->deleteLater();
   thread->quit();
 
   return app.exec();
}
 

выводит только

Код:
Before exec
After exec

либо, если между deleteLater() и quit() будет достаточно времени (например, пауза или логика какая)

Код:
Before exec
Deleted
QThread: Destroyed while thread is still running
After exec


Название: Re: Как завершить поток таймера?
Отправлено: Bepec от Август 30, 2017, 17:34
Вы правы, после запуска потока deleteLater просто не работает.
Видимо у него нет критериев для QThread, по которым он может освободить объект. Что весьма странно, ведь в старых версиях утечек памяти у меня не было.
Зато простой delete работает на отлично.


Нашёл в исходниках вот такую строчечку
Цитировать
Since Qt 4.8, if deleteLater() is called on an object that lives in a
    thread with no running event loop, the object will be destroyed when the
    thread finishes.

и вот такую
Цитировать
 From Qt 4.8 onwards, it is possible to deallocate objects that
    live in a thread that has just ended, by connecting the
    finished() signal to QObject::deleteLater().

Могу ошибаться, но получается из 1 цитаты, что объект потока удалится лишь после окончания работы потока, в которым был вызван ( основного потока).
А из второй безболезненное решение - вешать finished на deleteLater QObject'ов, которые живут в потоке, в том числе и таймеров.

PS но вопрос открыт - как же не "ручным" способом удалять поток :)


Название: Re: Как завершить поток таймера?
Отправлено: Bepec от Август 30, 2017, 17:57
Пока у меня только вариант вешать на finished потока слот с delete sender'a.


Название: Re: Как завершить поток таймера?
Отправлено: demaker от Декабрь 12, 2017, 15:02
Ответ = вызов в нём deleteLater и quit.

Предположим, что deleteLater() для экземпляра thread отработал, это значит еще не был завершен его метод run() и exec() в другом потоке, что может быть чревато последствиями - QThread: Destroyed while thread is still running и все что описано ранее в примере 1.
Если же сначала завершить поток, а затем вызвать deleteLater(), то экземпляр thread не будет удален совсем - утечки памяти.

Простой пример

Код
C++ (Qt)
class Thread
: public QThread
{
public:
   ~Thread ()
   {
       ::std::cout << "Deleted" << ::std::endl;
   }
 
   virtual void run ()
   {
       ::std::cout << "Before exec" << ::std::endl;
       exec();
       ::std::cout << "After exec" << ::std::endl;
   }
};
 
int main ( int argc, char ** argv )
{
   QCoreApplication app( argc, argv );
 
   QThread * thread = new Thread;
   thread->moveToThread( thread );
   thread->start();
   thread->deleteLater();
   thread->quit();
 
   return app.exec();
}
 

выводит только

Код:
Before exec
After exec

либо, если между deleteLater() и quit() будет достаточно времени (например, пауза или логика какая)

Код:
Before exec
Deleted
QThread: Destroyed while thread is still running
After exec


Поправьте меня - возможно я не прав, но
возможно так лучше будет
Код
C++ (Qt)
thread->quit();
thread->wait();
delete thread;
 

И тогда вот этого не будет
Код:
QThread: Destroyed while thread is still running


Название: Re: Как завершить поток таймера?
Отправлено: Kurles от Декабрь 12, 2017, 16:20
По мне дык наследование от QThread и переопределение в нем метода run() не очень хорошая практика. Использую механизм "воркеров", как то всё прозрачней получается, и проще следить за временем жизни объектов. Вот синтетический пример:

Код
C++ (Qt)
#include <QCoreApplication>
#include <QThread>
#include <QTimer>
#include <QDebug>
 
class Worker : public QObject {
   Q_OBJECT
public:
   Worker(QObject *parent = 0) : QObject(parent), mTimer(0) {}
   ~Worker() { qDebug() << Q_FUNC_INFO; }
 
   Q_INVOKABLE void start() {
       // для того, что бы создать таймер в контексте потока, в который перемещён
       // воркер, используем QMetaObject::invokeMethod
       if (QThread::currentThread() != thread()) {
           QMetaObject::invokeMethod(this, "start", Qt::QueuedConnection);
           return;
       }
       mTimer = new QTimer(this);
       connect(mTimer, &QTimer::timeout,
               this, &Worker::onTimeout);
       mTimer->start(1000);
   }
 
signals:
   void timeout();
 
private:
   void onTimeout() {
       qDebug() << "worker" << QThread::currentThreadId();
       emit timeout();
   }
 
private:
   QTimer *mTimer;
 
};
 
class Test : public QObject
{
   Q_OBJECT
public:
   Test(QObject *parent = 0) :
       QObject(parent),
   mWorker(new Worker())
   {
   connect(mWorker, &Worker::timeout,
           this, &Test::onTimeout,
           Qt::QueuedConnection);
   }
   ~Test()
   {
       // планируем удаление воркера
       mWorker->deleteLater();
       if (mThread.isRunning()) {
           // рубим поток
           mThread.quit();
           // даём время уничтожится воркеру
           mThread.wait();
       }
       qDebug() << Q_FUNC_INFO;
   }
 
   void start() {
       // перемещаем воркер в отдельный поток
       mWorker->moveToThread(&mThread);
       // стратуем поток
       mThread.start();
       // и воркер
       mWorker->start();
       // планируем через 5 секунд выход из тестового приложения
       QTimer::singleShot(5000, this, &Test::onQuitTimeout);
   }
 
 
   void onTimeout() {
       qDebug() << "test" << QThread::currentThreadId();
   }
 
   void onQuitTimeout() {
       qApp->quit();
   }
 
private:
   QThread mThread;
   Worker *mWorker;
 
};
 
int main(int argc, char *argv[])
{
   QCoreApplication a(argc, argv);
 
   Test t;
   t.start();
 
   return a.exec();
}
 
#include "main.moc"
 
 


Название: Re: Как завершить поток таймера?
Отправлено: ssoft от Декабрь 12, 2017, 16:42
Здесь смущает только место использования

Код
C++ (Qt)
mWorker->deleteLater();
 

Если поток уже завершен, то event loop уже остановлена и некому выполнять deleteLater).
А так, использование Worker в разных вариациях предпочтительнее. Наследование от потока тоже корректно, здесь на вкус и цвет, кому как удобнее.


Название: Re: Как завершить поток таймера?
Отправлено: ssoft от Декабрь 12, 2017, 16:48
Поправьте меня - возможно я не прав, но
возможно так лучше будет
Код
C++ (Qt)
thread->quit();
thread->wait();
delete thread;
 

И тогда вот этого не будет
Код:
QThread: Destroyed while thread is still running


Так тоже будет корректно, но тогда уж лучше сразу переменную на стеке делать и не мучаться с new/delete.



Название: Re: Как завершить поток таймера?
Отправлено: Kurles от Декабрь 12, 2017, 17:02
Здесь смущает только место использования

Код
C++ (Qt)
mWorker->deleteLater();
 

Если поток уже завершен, то event loop уже остановлена и некому выполнять deleteLater).
А так, использование Worker в разных вариациях предпочтительнее. Наследование от потока тоже корректно, здесь на вкус и цвет, кому как удобнее.
Поток останавливается ниже в деструкторе. Вызов QThread::quit и последующий вызов QThread::wait() гарантирует как минимум еще одну итерацию event loop потока, которая в свою очередь обеспечит удаление все запланированных к удалению объектов, привязанных к данному потоку.


Название: Re: Как завершить поток таймера?
Отправлено: Old от Декабрь 12, 2017, 17:17
последующий вызов QThread::wait() гарантирует как минимум еще одну итерацию event loop потока
wait ничего не гарантирует, даже более он сам блокирует поток в котором запущен до завершения ожидаемой нитки. В это время eventloop не крутиться.


Название: Re: Как завершить поток таймера?
Отправлено: Kurles от Декабрь 12, 2017, 17:36
последующий вызов QThread::wait() гарантирует как минимум еще одну итерацию event loop потока
wait ничего не гарантирует, даже более он сам блокирует поток в котором запущен до завершения ожидаемой нитки. В это время eventloop не крутиться.
то есть связка последовательных вызовов QThread::quit + QThread::wait не гарантирует итерации eventLoop'а? Как тогда корректно завершить поток с удалением всех его children'ов?


Название: Re: Как завершить поток таймера?
Отправлено: demaker от Декабрь 12, 2017, 18:01
последующий вызов QThread::wait() гарантирует как минимум еще одну итерацию event loop потока
wait ничего не гарантирует, даже более он сам блокирует поток в котором запущен до завершения ожидаемой нитки. В это время eventloop не крутиться.


Т.е Вы хотите сказать тем самым, что в книге Боровский А.Н. Qt 4.7+ Практическое программирование на С++ (2012г.) стр 169 Листинг 5.3 (глава 5) НЕ ВЕРЕН И В НЕМ ОШИБКА.


Название: Re: Как завершить поток таймера?
Отправлено: demaker от Декабрь 12, 2017, 18:03
Поправьте меня - возможно я не прав, но
возможно так лучше будет
Код
C++ (Qt)
thread->quit();
thread->wait();
delete thread;
 

И тогда вот этого не будет
Код:
QThread: Destroyed while thread is still running


Так тоже будет корректно, но тогда уж лучше сразу переменную на стеке делать и не мучаться с new/delete.



Вы имейти  ввиду переменную на стеке - т.е в run


Название: Re: Как завершить поток таймера?
Отправлено: kuzulis от Декабрь 12, 2017, 18:22
Wait не нужен если используется концепция с worker-ами, достаточно только quit(). Все там удалится как надо (если были коннекты к deleteLater() у воркета и у треда).


Название: Re: Как завершить поток таймера?
Отправлено: Old от Декабрь 12, 2017, 18:24
то есть связка последовательных вызовов QThread::quit + QThread::wait не гарантирует итерации eventLoop'а?
Ну откройте уже исходники. :)

wait блокирует выполнения нитки в котором он вызван, до завершения ожидаемой нитки.

Код
C++ (Qt)
bool QThread::wait(unsigned long time)
{
   Q_D(QThread);
   QMutexLocker locker(&d->mutex);
 
   if (d->thread_id == pthread_self()) {
       qWarning("QThread::wait: Thread tried to wait on itself");
       return false;
   }
 
   if (d->finished || !d->running)
       return true;
 
   while (d->running) {
       if (!d->thread_done.wait(locker.mutex(), time))
           return false;
   }
   return true;
}
 


Название: Re: Как завершить поток таймера?
Отправлено: Old от Декабрь 12, 2017, 18:26
Т.е Вы хотите сказать тем самым, что в книге Боровский А.Н. Qt 4.7+ Практическое программирование на С++ (2012г.) стр 169 Листинг 5.3 (глава 5) НЕ ВЕРЕН И В НЕМ ОШИБКА.
Честно говоря, я понятия не имею, что там написано. :)
Легко может быть ошибка.


Название: Re: Как завершить поток таймера?
Отправлено: ssoft от Декабрь 13, 2017, 08:20
Вы имейти  ввиду переменную на стеке - т.е в run

Я имею в виду такие костыли).

Код
C++ (Qt)
MyThread thread;
thread.moveToThread( &thread );
thread.start();
...
thread.quit();
thread.wait();
 

или даже такие

Код
C++ (Qt)
class MyThread : public QThread
{
public:
   MyThread ()
   {
       moveToThread( this );
   }
 
   ~MyThread ()
   {
       quit();
       wait();
   }
...
};
 
MyThread thread;
thread.start();
...
 


А по хорошему используйте QtConcurrent.

Wait не нужен если используется концепция с worker-ами, достаточно только quit(). Все там удалится как надо (если были коннекты к deleteLater() у воркета и у треда).

По-моему это уже обсуждали http://www.prog.org.ru/index.php?topic=31557.msg233459#msg233459 или что-то другое имеется в виду?


Название: Re: Как завершить поток таймера?
Отправлено: kuzulis от Декабрь 13, 2017, 09:22
Цитировать
По-моему это уже обсуждали http://www.prog.org.ru/index.php?topic=31557.msg233459#msg233459 или что-то другое имеется в виду?

Там в примере неправильно использован концепт. В концепте deleteThread() должен вызываться только по сигналам finished() и никак иначе (иначе можно и Машку за ляшку и козу на возу  :) ).


Название: Re: Как завершить поток таймера?
Отправлено: ssoft от Декабрь 13, 2017, 12:02
Ну то бишь верно будет так:

Код
C++ (Qt)
   QThread thread;
   Worker * worker = new Worker;
   worker->moveToThread( &thread );
   QObject::connect( &thread, SIGNAL( finished() ), worker, SLOT( deleteLater() ) );
   thread.start();
...
   thread.quit();
   thread.wait();
 

Это будет работать, так как finished() вызывается в потоке, в котором функционирует worker.
При этом важно, чтобы connect был выполнен по умолчанию (с опцией Qt::AutoConnection).

При этом wait() обязателен до удаления экземпляра thread, иначе снова может получиться)

Цитировать
QThread: Destroyed while thread is still running

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.


Название: Re: Как завершить поток таймера?
Отправлено: Old от Декабрь 13, 2017, 12:36
thread существует в контексте нити в которой он создавался, worker перемещается в контекст нити, которую создаст thread при запуске.
Если был автоматический коннект, то при генерации сигнала будет использоваться очередь рабочей нитки, которая к моменту finished будет уже остановлена и воркер разрушен не будет. :)


Название: Re: Как завершить поток таймера?
Отправлено: ssoft от Декабрь 13, 2017, 13:33
Происходит примерно следующее (Qt 5.9)

Случай Qt::AutoConnection определяет в runtime каким способом вызывать слот

Код
C++ (Qt)
...
           if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread)
               || (c->connectionType == Qt::QueuedConnection)) {
...
 

Слот deleteLater() помещает в очередь событие QDeferredDeleteEvent

Код
C++ (Qt)
   QCoreApplication::postEvent(this, new QDeferredDeleteEvent());
 

А QThread уже после вызова слота специальным способом обрабатывает оставшиеся сообщения

Код
C++ (Qt)
...
   emit thr->finished(QThread::QPrivateSignal());
   QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
...
 

Собственно специальная заплата самого Qt.