Как прослушать завершенную индикацию после перемещения выполнения в другой поток в Qt

У меня есть операция без графического интерфейса, которую я собираюсь выполнить в рабочем потоке. Погуглив, я нашел простой способ добиться этого здесь

В частности, QMetaObject::invokeMethod() меня вполне устраивает. Я также позаботился о том, чтобы не создавать подклассы QThread, а вместо этого создавать подклассы QObject для создания моего рабочего объекта, а затем перемещать привязку потока этого объекта к вновь созданному потоку. (подробно описано здесь

Теперь мой вопрос:

Я начну ставить методы в очередь из своего основного потока (предполагаю, что это происходит под капотом с помощью QEventLoop, хотя я его не устанавливал и не переустанавливал QThread::run() и не вызывал QThread::exec()). Мне нужно выяснить, когда мой рабочий закончил обработку. Другими словами, мне нужно знать, когда QEventLoop пуст. Я подумал, что могу использовать для этого сигнал finish(). Но это не работает. Может ли кто-нибудь пролить свет на то, как узнать, когда рабочий поток завершил выполнение? Если вам нужна дополнительная информация/код, просто дайте мне знать.

Плагин класса является подклассом QObject.

Plugin::Plugin()
{
    m_workerThread = new QThread(this);
    m_workerThread->start(QThread::IdlePriority);
    m_worker = new DataWorker(this);
    connect(m_workerThread, SIGNAL(finished()), m_worker, SLOT(deleteLater()));    
    m_worker->moveToThread(m_workerThread);
}

...

Plugin::updateData()
{
....
if( true == QMetaObject::invokeMethod(m_worker, "RunFProcToGetData", Qt::QueuedConnection, Q_ARG(QString, foo)))
            {
                qDebug() << "successfully invoked RunFProcToGetData";
            }
            if( true == QMetaObject::invokeMethod(m_worker, "updateDataIntoDB", Qt::QueuedConnection, Q_ARG(QSqlDatabase, db),                                                Q_ARG(QString, foo)))
            {
                qDebug() << "successfully invoked updateDataIntoDB";
            }
....
}

В классе Dataworker

Dataworker::RunProcToGetData(const QString &foo)
{
// invoke a program to get the Data
}


Dataworker::updateDataIntoDB(const QSqlDatabase& db, const QString &foo)
{
// Update database 
}

Поэтому, когда больше не осталось updateDataIntoDB и RunFPrcToGetData для обработки, мне нужен триггер


person Spottsworth    schedule 05.11.2012    source источник
comment
опубликуйте код, я понятия не имею, что вы говорите   -  person opc0de    schedule 05.11.2012
comment
Вызывает ли простой вызов quit() в потоке сигнал terminated()? Если, конечно, вы не хотите, чтобы рабочий постоянно оставался живым, в этом случае вам придется создать свой собственный сигнал.   -  person cmannett85    schedule 05.11.2012
comment
@ cmannett85: Я хочу, чтобы рабочий был жив и не имел проблем с созданием собственного сигнала и его передачей. Но как мне узнать, когда вызывать quit() в потоке? Для этого я должен заглянуть внутрь QEventLoop, который автоматически настраивается после создания потока. API для этого не нашел.   -  person Spottsworth    schedule 05.11.2012
comment
Если вы хотите, чтобы ваш работник остался в живых, не звоните quit().   -  person cmannett85    schedule 05.11.2012
comment
@cmannett85: Да. Я не вызываю quit(). Но что мне нужно сейчас, так это указание, когда у моего воркера больше нет методов для выполнения и, вероятно, он находится в состоянии ожидания. Есть ли способ получить эту информацию?   -  person Spottsworth    schedule 05.11.2012
comment
m_worker = new DataWorker(this);.Remove this.movethread() завершится ошибкой, если установлен родитель.   -  person UmNyobe    schedule 05.11.2012
comment
@UmNyobe: Не могли бы вы немного рассказать о последствиях использования «нового DataWorker (this)»?   -  person Spottsworth    schedule 05.11.2012
comment
@UmNyobe: На самом деле без предложенных вами изменений я вставил операторы qDebug() в свой основной поток и рабочий объект (или, скорее, в функцию, которую я ожидаю запустить в рабочем потоке) и нашел два отдельных идентификатора потока. Значит ли это, что сходство потоков действительно изменилось?   -  person Spottsworth    schedule 05.11.2012
comment
В документе сказано, что это не работает. Кроме того, система родительского контроля Qt используется для того, чтобы объект мог автоматически воздействовать на другие объекты (когда он удаляется, когда отображаются виджеты и т. д.). Таким образом, у вас есть 1 поток, который выполняет m_worker.foo();, в то время как из-за слота другой поток выполняет m_worker.bar: условия гонки.   -  person UmNyobe    schedule 05.11.2012
comment
@UmNyobe: +1. Спасибо что подметил это.   -  person Spottsworth    schedule 06.11.2012


Ответы (1)


Вам не нужно повторно реализовывать QThread::run(), ваш код в порядке. По умолчанию run() выполняет цикл обработки событий и слоты Dataworker, поставленные в очередь (Qt:QueuedConnection). Цикл выполняется бесконечно, пока вы явно не остановите его с помощью thread->quit().

Я бы порекомендовал вам поместить некоторые сигналы уведомления в класс Dataworker, которые вы будете испускать из RunFProcToGetData(), updateDataIntoDB(),... Чтобы вы могли подключить сигнал уведомления из последнего вызванного метода в updateData() к нужному слоту (вероятно, в плагине). Это соединение также будет помещено в очередь (Qt:QueuedConnection используется по умолчанию, если объекты выполняются в разных потоках).

Таким образом, вы будете уведомлены, когда все методы в очереди из updateData() закончат выполнение. Вы можете вызывать updateData() несколько раз; m_workerThread будет либо запускать слоты Dataworker в очереди, либо простаивать. Не забудьте остановить поток и дождаться его завершения в деструкторе плагина:

Plugin::~Plugin() {
    ...
    m_workerThread_->quit();
    m_workerThread_->wait();
    ...
}
person Saša Mrvoš    schedule 28.08.2013