2013-03-26 4 views
1

Я пытаюсь сказать нить, чтобы выйти изящно. Для этой цели поток проверяет на каждой итерации глобальный логический флаг, который указывает, должен ли поток продолжить или выйти. Нить настройки, как это (код из http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/):Почему мои потоки не выходят изящно?

ImageFusionQt::ImageFusionQt(QWidget* parent) 
: QMainWindow(parent) 
{  

captureThread = new QThread(); 
captureWorker = new CaptureWorker(); 

// Connects the threads started() signal to the process() slot in the worker, causing it to start. 
connect(captureThread, SIGNAL(started()), captureWorker, SLOT(process())); 

// Connect worker finished signal to trigger thread quit, then delete. 
connect(captureWorker, SIGNAL(finished()), captureThread, SLOT(quit())); 
connect(captureWorker, SIGNAL(finished()), captureWorker, SLOT(deleteLater())); 

// Make sure the thread object is deleted after execution has finished. 
connect(captureThread, SIGNAL(finished()), captureThread, SLOT(deleteLater())); 

// Give QThread ownership of Worker Object 
captureWorker->moveToThread(captureThread); 
captureThread->start(); 

} 

CaptureWorker.cpp

void CaptureWorker::process() 
{ 
    while(true) 
    { 
    g_exit_lock->lockForRead(); 
    if(g_exit) 
    { 
     g_exit_lock->unlock(); 
     break; 
    } 
    g_exit_lock->unlock(); 
    } 
    qDebug() << "CaptureWorker: Exiting."; 
    emit finished(); 
} 

Теперь, когда я пытаюсь остановить поток, установив флаг верно в некоторой функции, то process() возвращает, но поток не заканчивается, и вызов wait() блокируется навсегда. Почему мой поток не заканчивается? Выход

g_exit_lock->lockForWrite(); 
g_exit = true; 
g_exit_lock->unlock(); 

QThread::sleep(15); 
qDebug() << "ct finished? " << captureThread->isFinished(); 

captureThread->wait(); 
qDebug() << "All threads stopped."; 

Logfile:

2013.03.26 09:29:22[D] CaptureWorker: Exiting. 
2013.03.26 09:29:37[D] ct finished? false 

UPDATE

Я сделал некоторые отладки и выяснил некоторые межжала вещи:

  • нити блоков в своем цикле обработки событий (QEventLoop :: Exec). Он ждет сигнала quit(), который, по-видимому, не получает.
  • Создан цикл событий потока после process() возвращается. Подключив сигналы, как и я, метод run() потока вызывается после того, как поток уже завершил выполнение своей работы (например, process()).
  • Цикл событий очищает все опубликованные события выхода до выполнения фактического цикла.

Мой вывод

  • Подключение бросить курить() слот, как я не работает, потому что бросить курить() события удаляются, когда цикл событий устанавливается
  • Вызов бросить курить член() функция непосредственно на объект потока, по-видимому, приводит к изящному завершению. Это можно сделать извне или изнутри с помощью QThread :: currentThread() -> quit();

Открытые вопросы

  • Есть ли способ вызова метода процесса() после того, как цикл событий было установлено?
  • Неправильно, что цикл событий создается, когда работа выполнена. Однако, как я использую QThread в соответствии с docs
+0

Возможно, вам просто нужно очистить поток отладки? – Nick

+0

Что такое готовое()? –

+0

Регистрация - очень * непростой способ отладки программ. Узнайте, как использовать отладчик. Просто присоедините отладчик, сломайте программу, переключите контекст на рабочий поток и узнайте, что он делает из трассировки стека. С большими шансами, что вы теперь обнаружите, что он не выполняет цикл, но похоронен в каком-то OS-вызове, ожидая завершения захвата. Поэтому он не тестирует g_exit, поэтому он не уходит. –

ответ

2

Что здесь происходит quit не вызывается вообще. Вы знаете, что у Qt есть прямое или поставленное в очередь соединение. Когда вы используете это

connect(captureWorker, SIGNAL(finished()), captureThread, SLOT(quit())); 

это действительно автоматический тип подключения. Так как вы переместили captureWorker в другой поток, используется Qt::QueuedConnecton. quit() будет вызываться на главной линии .Но то, что вы делаете, блокирует основной поток: captureThread->wait();
quit() поставлен в очередь в цикле событий, но он заблокирован, потому что он ждет завершения потока.
Таким образом, вы можете позвонить бросить() непосредственно, как путем замены

connect(captureWorker, SIGNAL(finished()), captureThread, SLOT(quit())); 

с

connect(captureWorker, SIGNAL(finished()), captureThread, SLOT(quit()), Qt::DirectConnection); 

Или позвоните бросить() непосредственно, как вы предложили в редактировании, или вы также можете сделать что-то вроде следующий вместо captureThread->wait(); если вам действительно нужно

QEventLoop l; 
    l.connect(captureThread, SIGNAL(finished()), SLOT(quit())); 
    l.exec(QEventLoop::ExcludeUserInputEvents); 

в качестве альтернативы вы можете подключиться, чтобы нить готового сигнала() и сделать белым вы бы хотели сделать после captureThread->wait();, так что вам не придется ждать вручную вообще.
Существует много способов делать вещи в Qt

+0

Спасибо за ваш вклад. Кстати, я обновил свой вопрос, пока вы дали этот ответ. Я пытаюсь понять ваш ответ: я знаю, что captureThread живет в основном потоке, но вызов его слота quit() должен, тем не менее, выйти из потока, который он управляет, нет? И я не понимаю, как ваше предложение лучше, чем просто делать captureThread-> quit(). Можете ли вы расширить это? Большое спасибо. – binford

+0

Да, вызов quit() остановит цикл события. Дело в том, где его называют. Поскольку captureThread живет в основном потоке, вы должны вызывать его методы в основном потоке, потому что классы Qt являются реентерабельными. – spiritwolfform

+0

Кстати, я не думаю, что возможно, что цикл событий потока создается после возврата процесса(). Возможно, вы перепутали цикл событий основного потока и цикл событий потока рабочего пользователя – spiritwolfform

0

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

g_exit_lock->lockForWrite(); 
g_exit = true; 
g_exit_lock->unlock(); 

QThread::sleep(15); 
qDebug() << "ct finished? " << captureThread->isFinished(); 

// You are missing below line, without this, main thread will block 
captureThread->quit(); 
captureThread->wait(); 

qDebug() << "All threads stopped."; 
Смежные вопросы