Qt中的线程应用程序

时间:2017-06-29 18:36:19

标签: c++ multithreading qt

我正在学习Qt中的线程管理,所以如果我犯了琐碎的错误,请向我道歉。让我们回到主题。

简短说明:

我编写了一个小应用程序来测试线程的工作方式。我有简单的GUI界面和两个按钮。他们每个人都可以启动和停止不同的线程。主题基于this link下的工作人员类。我正在使用 Ubuntu 16.04 LTS x64 下的 Qt Creator 编译我的代码。

主要问题:

  1. 当我尝试启动线程时,它正常工作。然后我停下来尝试重新开始。在这种情况下,线程无法启动。 Maby我正在以不正确的方式关闭线程吗?
  2. 这种用线程管理的方式是否合适?它做得好吗?我读到有关将QObject移动到线程在Qt中比子类线程更好。 (但这取决于我知道)。
  3. 我的代码:

    worker.h:

    #ifndef WORKER_H
    #define WORKER_H
    
    #include <QObject>
    #include <QMutex>
    
    class Worker : public QObject
    {
        Q_OBJECT
    public:
        explicit Worker(QObject *parent = nullptr);
    
        void stop();
        void setMessage(const QString &message);
    
    signals:
        void finished();
    
    public slots:
        void process();
    
    private:
        volatile bool stopped;
        QString messageStr;
        QMutex mutex;
    };
    
    #endif // WORKER_H
    

    worker.cpp:

    #include "worker.h"
    #include <QDebug>
    
    Worker::Worker(QObject *parent)
        : QObject(parent)
    {
        stopped = false;
    }
    
    void Worker::process()
    {
        forever
        {
            mutex.lock();
            if(stopped)
            {
                stopped = false;
                mutex.unlock();
                break;
            }
            mutex.unlock();
            qDebug() << messageStr;
        }
        emit finished();
    }
    
    void Worker::stop()
    {
        mutex.lock();
        stopped = true;
        mutex.unlock();
    }
    
    void Worker::setMessage(const QString &message)
    {
        messageStr = message;
    }
    

    mydialog.h:

    #ifndef MYDIALOG_H
    #define MYDIALOG_H
    
    #include <QDialog>
    
    class Worker;
    
    class MyDialog : public QDialog
    {
        Q_OBJECT
    public:
        MyDialog(QWidget *parent = nullptr);
    
    private slots:
        void startStopThreadA();
        void startStopThreadB();
        void showWorkingGUI();
    
    private:
        Worker *workerA;
        Worker *workerB;
        QThread *threadA;
        QThread *threadB;
        QPushButton *threadAButton;
        QPushButton *threadBButton;
        QPushButton *quitButton;
    
    };
    
    #endif // MYDIALOG_H
    

    mydilog.cpp:

    #include "mydialog.h"
    #include "worker.h"
    
    #include <QCloseEvent>
    #include <QHBoxLayout>
    #include <QPushButton>
    #include <QThread>
    #include <QDebug>
    #include <QTimer>
    
    MyDialog::MyDialog(QWidget *parent)
        : QDialog(parent)
    {
        //Here bla bla for gui
        threadAButton = new QPushButton(tr("Start A"));
        threadBButton = new QPushButton(tr("Start B"));
        quitButton = new QPushButton(tr("Quit"));
    
        connect(threadAButton, SIGNAL(clicked()), this, SLOT(startStopThreadA()));
        connect(threadBButton, SIGNAL(clicked()), this, SLOT(startStopThreadB()));
    
        QHBoxLayout *layout = new QHBoxLayout;
        layout->addWidget(threadAButton);
        layout->addWidget(threadBButton);
        layout->addWidget(quitButton);
    
        setLayout(layout);
    
        //Create worker's instances
        workerA = new Worker;
        workerB = new Worker;
    
        workerA->setMessage("Thread 1");
        workerB->setMessage("Thread 2");
    
        //Create threads instances
        threadA = new QThread;
        threadB = new QThread;
    
        //Move worker to thread
        workerA->moveToThread(threadA);
        workerB->moveToThread(threadB);
    
        connect(threadA, SIGNAL(started()), workerA, SLOT(process()));
        connect(workerA, SIGNAL(finished()), threadA, SLOT(quit()));
        connect(workerA, SIGNAL(finished()), workerA, SLOT(deleteLater()));
        connect(threadA, SIGNAL(finished()), threadA, SLOT(deleteLater()));
    
        connect(threadB, SIGNAL(started()), workerB, SLOT(process()));
        connect(workerB, SIGNAL(finished()), threadB, SLOT(quit()));
        connect(workerB, SIGNAL(finished()), workerB, SLOT(deleteLater()));
        connect(threadB, SIGNAL(finished()), threadB, SLOT(deleteLater()));
    
        QTimer *timer = new QTimer(this);
        connect(timer, SIGNAL(timeout()), this, SLOT(showWorkingGUI()));
        timer->start(1000);
    
    }
    
    void MyDialog::startStopThreadA()
    {
        if(threadA->isRunning())
        {
            workerA->stop();
            threadAButton->setText(tr("Start A"));
        }
        else
        {
            threadA->start();
            threadAButton->setText(tr("Stop A"));
        }
    }
    
    void MyDialog::startStopThreadB()
    {
        if(threadB->isRunning())
        {
            workerB->stop();
            threadBButton->setText(tr("Start B"));
        }
        else
        {
            threadB->start();
            threadBButton->setText(tr("Stop B"));
        }
    }
    
    void MyDialog::showWorkingGUI()
    {
        qDebug() << "GUI Thread works!";
    }
    

    和main.cpp:

    #include <QApplication>
    #include "mydialog.h"
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        MyDialog dialog;
        dialog.show();
        return a.exec();
    }
    

    请寻求帮助。如果我犯了一些错误,不要嘲笑我。我是线程中的新手:)

    修改 我忘了附上图片: enter image description here

1 个答案:

答案 0 :(得分:0)

在按钮方法中创建新对象而不是MyDialog ctor解决了我的问题。例如:

void MainWindow::startStopThreadA()
{
    //First method - start/stop thread
    if(threadA && threadA->isRunning())
    {
        workerA->stop();
        threadA = nullptr;
        ui->threadAButton->setText("Start A");
    }
    else
    {
        threadA = new QThread;
        workerA = new WorkerObject;
        workerA->setMessage("Thread A running");
        workerA->moveToThread(threadA);

        connect(threadA, SIGNAL(started()), workerA, SLOT(process()), Qt::QueuedConnection);
        connect(workerA, SIGNAL(finished()), threadA, SLOT(quit()));
        connect(workerA, SIGNAL(finished()), workerA, SLOT(deleteLater()));
        connect(threadA, SIGNAL(finished()), threadA, SLOT(deleteLater()));

        threadA->start();
        ui->threadAButton->setText("Stop A");
    }
}