2016-06-24 4 views
1

Создал QThread в main() i.e Основной поток. Переместил рабочий класс в новый поток. Нить выполняет метод «StartThread» рабочего класса.Stop Qt Thread: вызов exit() или quit() не останавливает выполнение потока

Worker Thread:

//header file 
class Worker : public QObject 
{ 
    Q_OBJECT 
public: 
    Worker(QThread* thread); 

public slots: 
    void StartThread(); 
    void EndThread(); 

private: 
    QThread* m_pCurrentThread; 
}; 


// source file 
#include "QDebug" 
#include "QThread" 
#include "worker.h" 

Worker::Worker(QThread* thread) 
{ 
m_pCurrentThread = thread; 
} 

void Worker::StartThread() 
{ 
    qDebug() << " StartThread"; 

    while(true) 
    { 
     QThread::msleep(1000); 
     qDebug() << " thread running"; 
     static int count = 0; 
     count++; 
     if(count == 10) 
     {    
      break; 
     } 
     if(m_pCurrentThread->isInterruptionRequested()) 
     { 
      qDebug() << " is interrupt requested"; 
      // Option 3: 
      m_pCurrentThread->exit();    
     } 
    } 
    qDebug() << "StartThread finished"; 
} 

void Worker::EndThread() 
{ 
    qDebug() << "thread finished"; 
} 

main.cpp

#include <QCoreApplication> 
#include "worker.h" 
#include "QThread" 
#include "QObject" 
#include "QDebug" 

int main(int argc, char *argv[]) 
{ 
    QCoreApplication a(argc, argv); 

    QThread* thread = new QThread(); 
    Worker* workerObj = new Worker(thread); 

    QObject::connect(thread, 
        SIGNAL(started()), 
        workerObj, 
        SLOT(StartThread())); 
    QObject::connect(thread, 
        SIGNAL(finished()), 
        workerObj, 
        SLOT(EndThread())); 



    workerObj->moveToThread(thread); 
    thread->start(); 
    thread->requestInterruption(); 
    QThread::msleep(2000); 
    qDebug() << "terminate thread"; 
    if(thread->isRunning()) 
    { 
     // Option 1,2 exit()/quit() used but got same output 
     thread->exit(); 
     qDebug() << "wait on thread"; 
     thread->wait(); 
    } 
    qDebug() << " exiting main"; 

    return a.exec(); 
} 

Теперь до завершения 'StartThread' и нить вышла грациозно я хочу, чтобы остановить поток из главного потока. Используется,

  1. резьбовой> Выход() и ждал (резьбовые> ждать()) в главном потоке.
  2. thread-> quit() и ждал (нить-> подождать()) в главной теме.

  3. выход() в 'StartThread'

Ожидаемое: Казнь поток должен остановиться, как только выход()/выхода() вызывается из главного потока.

Фактический: Во всех трех случаях поток продолжает работать, завершает его метод «StartThread» и выходит изящно. Так же хорошо, как не выходил exit()/quit(). выход:

StartThread 
thread running 
is interrupt requested 
terminate thread 
wait on thread 
thread running 
is interrupt requested 
thread running 
is interrupt requested 
thread running 
is interrupt requested 
thread running 
is interrupt requested 
thread running 
is interrupt requested 
thread running 
is interrupt requested 
thread running 
is interrupt requested 
thread running 
is interrupt requested 
thread running 
StartThread finished 
thread finished 
exiting main 

Можно ли остановить/утилизировать нить, как в случае необходимости в главном потоке?

ответ

-2

Вы объявили и инициализировали count внутри цикла while. Переместите его снаружи, и он остановит повторную инициализацию до нуля на каждой итерации и, таким образом, уйдет вовремя. Я не понимаю, почему вы объявили его static.

Примечание
Если вы собираетесь использовать в очереди сигналов и обработки событий, убедитесь, что вы периодически вызывать processEvents на отправителе событий в нити внутри while цикла:

if (thread()->eventDispatcher()->hasPendingEvents() 
    thread()->eventDispatcher()->processEvents(); 
3

isInterruptionRequested только возвращает истину, если вы» ve под названием QThread::requestInterruption, неQThread::quit. Это документировано.

Если вы хотите использовать QThread::quit, поток должен вращать цикл событий, поэтому вы не должны извлекать из него; вместо этого сделайте следующее:

class Worker : public QObject { 
    QBasicTimer m_timer; 
    int chunksToDo = 20; 
    void doChunkOfWork() { 
    QThread::sleep(1); 
    } 
    void timerEvent(QTimerEvent * ev) { 
    if (ev->timerId() != m_timer.timerId()) return; 
    if (!chunksToDo) { m_timer.stop(); return; } 
    doChunkOfWork(); 
    if (!--chunksToDo) emit done(); 
    } 
public: 
    explicit Worker(QObject * parent = nullptr) : QObject{parent} { 
    m_timer.start(0, this); 
    } 
    Q_SIGNAL void done(); 
}; 

Чтобы запустить рабочего, создайте его и переместите его в какую-то его нить. Чтобы остановить работника, просто quit() его нить до того, как рабочий закончится.

int main(int argc, char ** argv) { 
    QCoreApplication app{argc, argv}; 
    Worker worker; 
    QThread thread; 
    worker.moveToThread(&thread); 
    QObject::connect(&worker, &Worker::done, &thread, &QThread::quit); 
    QObject::connect(&thread, &QThread::finished, &app, &QCoreApplication::quit); 
    thread.start(); 
    return app.exec(); 
} 

Вместо того чтобы управлять жизнью потока вручную, возможно, вы захотите использовать пул потоков.

class Worker : public QObject, QRunnable { 
    int chunksToDo = 20; 
    volatile bool active = true; 
    void doChunkOfWork() { 
    QThread::sleep(1); 
    } 
    void run() { 
    while (active && chunksToDo) { 
     doChunkOfWork(); 
     --chunksToDo; 
    } 
    emit done(); 
    } 
public: 
    using QObject::QObject; 
    Q_SLOT void stop() { active = false; } 
    Q_SIGNAL void done(); 
}; 

int main(int argc, char ** argv) { 
    QCoreApplication app{argc, argv}; 
    QThreadPool pool; 
    Worker worker; 
    worker.setAutoDelete(false); // important! 
    pool.start(&worker); 
    // * 
    QTimer::singleShot(5000, &worker, &Worker::stop); 
    QObject::connect(&worker, &Worker::done, &app, &QCoreApplication::quit); 
    return app.exec(); 
} 

Альтернативный способ закончить его без запуска основного цикла событий были бы:

// * 
    QThread::sleep(5); 
    worker.stop(); 
    pool.waitForDone(); 
} 

This answer имеет полный пример потоковые несколько заданий для пула потоков.

1

У вас, похоже, много заблуждений.

Из документов Qt, как QThread::quit() и QThread::exit():

Сообщите цикл событий Нити для выхода

Это означает, что если цикл обработки событий Нити является не работает (либо QThread::exec() не был или цикл цикла занят выполнением некоторой тяжелой функции блокировки (например, ваш StartThread)), нить не будет завершена до тех пор, пока элемент управления не вернется в цикл событий. Я думаю, это объясняет, почему quit() и exit() не сработали для вас, как ожидалось.

Также Вы, кажется, использует QThread::requestInterruption() в неправильном направлении, назад к Qt docs:

Запрос прерывания потока. Этот запрос является рекомендательным, и в цепочку работает поток, чтобы решить, как и как он должен действовать в отношении такого запроса. Эта функция не останавливает какой-либо цикл событий в потоке и не прерывает его каким-либо образом.

так что это на самом деле не делать ничего с вашей теме, она просто вызывает последующие вызовы QThread::isInterruptionRequested() вернуться true, так что, когда вы обнаружили, что (в потоке), вы должны выполнить очистку и выйти в ближайшее время насколько это возможно (снова для того, чтобы работать здесь quit(), цикл событий должен быть запущен, поэтому вы должны вернуться из функции StartThread здесь, чтобы вернуть поток обратно в цикл событий).

Теперь, чтобы ответить на ваш вопрос:

Можно ли остановить/утилизировать нить, как в случае необходимости в главном потоке?

Чтобы сделать это, вы должны либо избегать таких длительных функций и убедитесь, что вы вернетесь в цикл обработки событий, как можно скорее, так что quit()/exit() может работать. Или выполните очистку и возврат из текущей функции, как только вы обнаружите, что isInterruptionRequested() возвращает true, так что после этого могут работать вызовы quit()/exit().

Как указано @kuba, вы можете использовать пул потоков вместо управления жизнью вашего потока вручную.

P.S.вам не нужно хранить указатель на QThread внутри Worker, чтобы использовать isInterruptionRequested(). вы можете использовать QThread::currentThread()->isInterruptionRequested() вместо m_pCurrentThread->isInterruptionRequested() (то же самое относится к вашему вызову на выход m_pCurrentThread->isInterruptionRequested())