在QThread中中止worker对象

时间:2016-11-03 12:10:19

标签: qt5 wait qthread worker qt5.5

我知道有很多关于这个主题的帖子,我已经读过它们,并认为我也理解它们。但我仍然遇到在QThread中中止QThread或更确切地说是一个worker对象的问题。

我有一个GUI应用程序和一个库。 GUI可以请求库执行和中止工作对象(连接到WorkerHandler插槽)。 WorkerHandler可以创建几个都从基类继承的WorkerObject。我试图减少这个例子的代码,但它仍然是某种冗长的。

Gui.h

class Gui : public QMainWindow
{
    Q_OBJECT

public:
    Gui(QWidget *parent = 0);
    ~Gui();
private:

    Ui::GuiClass ui;
    QThread *workerHandlerThread;
    WorkerHandler *workerHandler;
    void connectActions();

signals:
    void execWorker(WorkerParams _params);
    void abortWorker(WorkerType type);

slots:
    void buttonExecPressed();
    void buttonAbortPressed();
}

Gui.cpp

void Gui::Gui()
{   
    ui.btnExecA->setProperty("type", QVariant::fromValue(WorkerType::A)); //WorkerType is just a enum, bin type to button
    ui.btnExecB->setProperty("type", QVariant::fromValue(WorkerType::B));
    ui.btnAbortA->setProperty("type", QVariant::fromValue(WorkerType::A));
    ui.btnAbortB->setProperty("type", QVariant::fromValue(WorkerType::B));
    connectActions();

    workerHandlerThread = new QThread();
    workerHandler = new WorkerHandler();
    workerHandler->moveToThread(workerHandlerThread); // move worker execution to another thread
    workerHandlerThread->start(); //start will call run and run will run the QEventLoop of QThread by calling exec
}

void Gui::~Gui()
{   
    workerHandlerThread->quit();
    workerHandlerThread->wait();
    delete workerHandlerThread;
    delete workerHandler;
}

void Gui::connectActions()
{
    connect(ui.btnExecA, &QPushButton::clicked, this, &Gui::buttonExecPressed);
    connect(ui.btnExecB, &QPushButton::clicked, this, &Gui::buttonExecPressed);
    connect(ui.btnAbortA, &QPushButton::clicked, this, &Gui::buttonAbortPressed);
    connect(ui.btnAbortB, &QPushButton::clicked, this, &Gui::buttonAbortPressed);   
    connect(this, &Gui::execWorker, workerHandler, &WorkerHandler::execWorker);
    connect(this, &Gui::abortWorker, workerHandler, &WorkerHandler::abortWorker);
}

void Gui::buttonExecPressed()
{
    QPushButton* button = qobject_cast<QPushButton*>(sender());
    if (button)
    {
        WorkerType type = button->property("type").value<WorkerType>(); //get worker type
        WorkerParams params = WorkerParamsFactory::Get()->CreateParams(type); //WorkerParamsFactory cretes default parameters based on type
        emit execWorker(params); //tell WorkerHandler to create a workerObject based on these parameters
    }
}

void Gui::buttonAbortPressed()
{
    QPushButton* button = qobject_cast<QPushButton*>(sender());
    if (button)
    {
        WorkerType type = button->property("type").value<WorkerType>(); 
        emit abortWorker(type); //tell WorkerHandler to abort a specific workerObject
    }
}

WorkerHandler.h

class WorkerHandler : public QObject {
    Q_OBJECT

public:
    WorkerHandler(QObject * parent = Q_NULLPTR);
    ~WorkerHandler();


public slots:
    void execWorker(WorkerParams _params);
    void abortWorker(WorkerType type);

private:
    QMap<WorkerType, WorkerObjectBase*> workerPool; //contains the workerobjects
};

WorkerHandler.cpp

void WorkerHandler::execWorker(WorkerParams _params)
{

    QThread *thread = new QThread();
    WorkerObjectBase *worker = WorkerObjectFactory::Get()->CreateWorker(_params);  //Factory to create specific Worker Object based on given params
    worker->moveToThread(thread);

    connect(thread, &QThread::started, workerThread, &WorkerObjectBase::process); 
    connect(workerThread, &WorkerObjectBase::workerFinished, thread, &QThread::quit);  //quit the QThread when worker is finished
    connect(thread, &QThread::finished, thread, &QThread::deleteLater); //free resources when thread is finished
    connect(thread, &QThread::finished, workerThread, &WorkerObjectBase::deleteLater);  //free resources when thread is finished

    workerPool.insert(_params.type, worker); //_params.type contains WorkerType

    thread->start(); //will call run of qthread which will call exec
}


void WorkerHandler::abortWorker(WorkerType type)
{
    WorkerObjectBase *worker = workerPool.value(type);
    worker->requestAbort();

    QThread *workerThread = worker->thread();

    if (workerThread)
    {
        if (!workerThread->wait(10000)) //will always block the 10 seconds and terminate the thread. using just wait() will block forever
        {
            workerThread->terminate();
        }
    }
}

WorkerHandlerBase.h

class WorkerObjectBase : public QObject {
    Q_OBJECT

public:
    WorkerObjectBase(QObject * parent = Q_NULLPTR);
    ~WorkerObjectBase();

    void requestAbort();
protected:
    //some WorkerObject basic parameters
    bool abortRequested();

public slots:
    virtual void process();

    signals:
           void workerFinished();   

private:
    QMutex abortMutex;
    bool abort = false;
};

WorkerHandlerBase.cpp

void WorkerObjectBase::requestAbort()
{
    abortMutex.lock();
    abort = true;
    abortMutex.unlock();
}

bool WorkerObjectBase::abortRequested()
{
    bool abortRequested;
    abortMutex.lock();
    abortRequested = abort;
    abortMutex.unlock();
    return abortRequested;
}

WorkerObjectA.h

class WorkerObjectA : public WorkerObjectBase {
    Q_OBJECT

public:
    WorkerObjectA(QObject * parent = Q_NULLPTR);
    ~WorkerObjectA();

protected:
    //some WorkerObjectA parameters

public slots:
    void process();
};

WorkerObjectA.cpp

void WorkerObjectA::process()
{
    while(!abortRequested())
    {
        //do some stuff
    }
    emit workerFinished();
}

问题是,当我使用wait时,它会阻止信号处理。未处理workerFinished,QThread不退出。但我仍然不明白为什么。当我创建一个新的工作对象时,我将它移动到另一个线程。当这个线程启动时,它运行自己的QEventLoop,如QThread

中所述

5.5文档:

  

void QThread :: run()

     

线程的起点。在调用start()后,新的   创建的线程调用此函数。简单的默认实现   调用exec()。

因此,即使我的WorkerHandler线程由于调用wait而阻塞,特定workerObject的QThread仍应设法获取workerFinished信号并调用退出槽。如果我根本不使用等待,一切都很好。但是当工作者对象进程方法中出现意外事件而导​​致它不能发出workerFinished时,我希望能够以艰难的方式杀死该线程。

那么,我做错了什么?

0 个答案:

没有答案