【问题标题】:Doing QThread-ing the "right" way以“正确”的方式进行 QThread-ing
【发布时间】:2015-01-26 19:01:08
【问题描述】:

使用这个程序,我按下“运行”按钮,for 循环循环 100 次(延迟 100 毫秒) 并在 txt 字段中打印循环计数

我已经使用从 QThread 派生的 MyThread 对象成功地完成了它。有用。我可以 使用“停止”按钮中断循环。

然而,从 QThread 派生一个对象是非常糟糕的。所以我做到了 他们建议的另一种方式,“正确”的方式。

而且它不起作用。我可以在控制台上得到循环周期数,但不能进入文本框

在 100 个循环完成之前,“运行”按钮会按下并不会再次出现。 并且文本字段显示 99。

这是代码,我做错了什么?

// MyWidget.h   SF022

#ifndef MYWIDGET_H_
#define MYWIDGET_H_

#include <QtWidgets/QWidget>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QPushButton>

#include "../MyThread.h"
#include "../MyObject.h"
#include <QThread>

class MyWidget : public QWidget {

    Q_OBJECT

public:
    MyWidget(QWidget* parent = 0);
    ~MyWidget();

    QPushButton* pbRun;
    QPushButton* pbStop;
    QPushButton* pbExit;
    QLineEdit*   txtCount;

    QThread* cThread;

    MyObject* myObject;

public slots:
    void onNumberChanged(int);

private slots:
    void pbRun_Slot();
    void pbStop_Slot();
    void pbExit_Slot();
};

#endif /* MYWIDGET_H_ */
------------------------------------------------
// MyWidget.cpp

#include "MyWidget.h"
#include "../K.h"
#include <iostream>

MyWidget::MyWidget(QWidget* parent) : QWidget(parent) {

    pbRun = new QPushButton(this);
    pbRun->setObjectName(QStringLiteral("pbRun"));
    pbRun->setGeometry(QRect(20, 20, 80, 40));
    pbRun->setText("Run");

    connect(pbRun, SIGNAL(clicked()), this, SLOT(pbRun_Slot()));

    pbStop = new QPushButton(this);
    pbStop->setObjectName(QStringLiteral("pbStop"));
    pbStop->setGeometry(QRect(20, 80, 80, 40));
    pbStop->setText("Stop");

    connect(pbStop, SIGNAL(clicked()), this, SLOT(pbStop_Slot()));

    pbExit = new QPushButton(this);
    pbExit->setObjectName(QStringLiteral("pbExit"));
    pbExit->setGeometry(QRect(20, 140, 80, 40));
    pbExit->setText("Exit");

    connect(pbExit, SIGNAL(clicked()), this, SLOT(pbExit_Slot()));

    txtCount = new QLineEdit(this);
    txtCount->setGeometry(QRect(20, 200, 80, 40));
    txtCount->setStyleSheet("QLineEdit{background: white;}");

//  myObject holds the cycling mechanism
    myObject = new MyObject(this);

//  the myObject sends each new cycle number out here
    connect(myObject, SIGNAL(numberChanged(int)), this, SLOT(onNumberChanged(int)));
}

MyWidget::~MyWidget() {
}

void MyWidget::pbRun_Slot() {

//  start thread

    cThread = new QThread(this);
    myObject->doSetup(*cThread);
    myObject->moveToThread(cThread);
    cThread->start();
}

void MyWidget::pbStop_Slot() {

//   stop the thread

   myObject->Stop = true;
}

void MyWidget::pbExit_Slot() {

//  a static pointer to the main window

   (K::SfMainWin)->close();
}

// a slot
void MyWidget::onNumberChanged(int j) {

//  output the cycle count to a text field
    txtCount->setText(QString::number(j));
}
----------------------------------------------------------
// MyObject.h

#ifndef MYOBJECT_H_
#define MYOBJECT_H_

#include <QObject>
#include <QThread>

class MyObject : public QObject {

    Q_OBJECT

public:
    explicit MyObject(QObject* parent = 0);
    ~MyObject();

    void doSetup(QThread&);

    bool Stop;

signals:
    void numberChanged(int);

public slots:
    void doWork();
};

#endif /* MYOBJECT_H_ */
----------------------------------------------------------
// MyObject.cpp    

#include "MyObject.h"
#include <QMutex>
#include <iostream>
#include "string.h"

MyObject::MyObject(QObject* parent) : QObject(parent) {

    Stop = false;
}

MyObject::~MyObject() {

}

void MyObject::doSetup(QThread& cThread) {

    Stop = false;

    connect(&cThread, SIGNAL(started()), this, SLOT(doWork()));
}

void MyObject::doWork() {

    for (int i = 0; i < 100; i++) {

        QMutex mutex;

        mutex.lock();
        if (this->Stop) {

            break;
        }

//  output into a text field
        emit numberChanged(i);

//  output on the console
        std::cout << "running " << (QString::number(i)).toStdString() << std::endl;

        mutex.unlock();

        QThread::msleep(100);

    }
}

【问题讨论】:

  • 查看以下链接以获得一些见解:mayaposch.wordpress.com/2011/11/01/…
  • 如果这只是一个练习,那么可以,但通常你会用QTimer 而不是工作线程来做这样的事情。阅读this了解更多信息。

标签: c++ multithreading qt qthread


【解决方案1】:

myObject 从未移动到您创建的线程。一切都在主线程中执行。因为

myObject = new MyObject(this);

要将 QObject 移动到另一个线程,he should not have a parent。如果确实如此,Qt 会静默告诉你出了什么问题(通过在输出上打印,与不正确的连接相同)。框架设计不要对这种类型的警告感到恐慌......

应该是的

myObject = new MyObject(0);

现在这已被清除,您的代码中还有其他缺陷。

  1. QMutex mutex; 是本地的,总是被同一个线程获取。这意味着他没有目的。相反,它应该是 MyObject 的私有成员
  2. MyWidget::pbStop_Slot 应该是MyObject 的方法,否则访问Stop 成员时会出现竞争条件。还记得上面的互斥锁吗?是时候使用它了。顺便说一句,你的实现直接调用方法,因为cThread的偶数循环只执行doWork

    MyObject::pbStop_Slot()
    {
        mutex.lock()
        Stop = true;
        mutex.unlock()
    }
    
  3. 现在您的程序在技术上应该是正确的。但是很糟糕,您不能使用信号和插槽,因为您的线程被阻止执行doWork。另外,我们能不能对那个锁做点什么。事实上,是的。我将采用的方法是使用 Qtimer 作为心跳 ech 100ms,而不是让线程休眠。但是为了不改变你的代码,你可以直接使用QAbstractEventDispatcher * QAbstractEventDispatcher::instance ( QThread * thread = 0 )

    MyObject::pbStop_Slot() //becomes a real slot again
    {
        // no more mutex
        Stop = true;
    }
    ....
    //this connection is changed
    connect(pbStop, SIGNAL(clicked()), myObject, SLOT(pbStop_Slot()));
    ....
    
    void MyObject::doWork() {
    
    for (int i = 0; i < 100; i++) {
    
        //no mutex
        if (this->Stop) {
    
            break;
        }
    
        //  output into a text field
        emit numberChanged(i);
    
        //  output on the console
        std::cout << "running " << (QString::number(i)).toStdString() << std::endl;
    
       //process events, to allow stop to be processed using signals and slots
       QAbstractEventDispatcher::instance(cThread)->processEvents();
    
        QThread::msleep(100);    
    }
    

    }

  4. 关于processEvents警告。就像现在一样,如果用户在执行 dowork 时按下 run它将在自身内部调用。你现在有一个讨厌的代码。避免这种情况的一种简单方法是在 dowork 开始时放置一个检查并设置的布尔值。

    dowork(){
      if(isdoingwork)
        return;
      isdoingwork = true
      for(...
    

这是实现reentrancy 的穷人方式。你会在 Qt 文档中经常看到 reentrant 这个词。

祝您在多线程之旅中好运。

【讨论】:

    【解决方案2】:

    非常好的乌姆尼奥贝!

    作为参考,我在此处添加了更正的代码。

    // MyWidget.h   
    
    #ifndef MYWIDGET_H_
    #define MYWIDGET_H_
    
    #include <QtWidgets/QWidget>
    #include <QtWidgets/QLineEdit>
    #include <QtWidgets/QPushButton>
    
    #include "../MyObject.h"
    #include <QThread>
    
    class MyWidget : public QWidget {
    
        Q_OBJECT
    
    public:
        MyWidget(QWidget* parent = 0);
        ~MyWidget();
    
        QPushButton* pbRun;
        QPushButton* pbStop;
        QPushButton* pbExit;
        QLineEdit*   txtCount;
    
        QThread* wThread;
    
        MyObject* myObject;
    
    public slots:
        void onNumberChanged(int);
    
    private slots:
        void pbRun_Slot();
    //  void pbStop_Slot();
        void pbExit_Slot();
    };
    
    #endif /* MYWIDGET_H_ */
    -----------------------------------------
    // MyWidget.cpp
    
    #include "MyWidget.h"
    #include "../K.h"
    #include <iostream>
    
    MyWidget::MyWidget(QWidget* parent) : QWidget(parent) {
    
        pbRun = new QPushButton(this);
        pbRun->setObjectName(QStringLiteral("pbRun"));
        pbRun->setGeometry(QRect(20, 20, 80, 40));
        pbRun->setText("Run");
    
        connect(pbRun, SIGNAL(clicked()), this, SLOT(pbRun_Slot()));
    
        pbStop = new QPushButton(this);
        pbStop->setObjectName(QStringLiteral("pbStop"));
        pbStop->setGeometry(QRect(20, 80, 80, 40));
        pbStop->setText("Stop");
    
        pbExit = new QPushButton(this);
        pbExit->setObjectName(QStringLiteral("pbExit"));
        pbExit->setGeometry(QRect(20, 140, 80, 40));
        pbExit->setText("Exit");
    
        connect(pbExit, SIGNAL(clicked()), this, SLOT(pbExit_Slot()));
    
        txtCount = new QLineEdit(this);
        txtCount->setGeometry(QRect(20, 200, 80, 40));
        txtCount->setStyleSheet("QLineEdit{background: white;}");
    
        myObject = new MyObject(0);
    
        connect(myObject, SIGNAL(numberChanged(int)), this, SLOT(onNumberChanged(int)));
    
        connect(pbStop, SIGNAL(clicked()), myObject, SLOT(pbStop_Slot()));
    }
    
    MyWidget::~MyWidget() {
    
        delete myObject;
        delete wThread;
    }
    
    void MyWidget::pbRun_Slot() {
    
    //  start QThread*, wThread in the MyWidget class
    
        wThread = new QThread(this);
        myObject->doSetup(wThread);
        myObject->moveToThread(wThread);
        wThread->start();
    }
    
    void MyWidget::pbExit_Slot() {
    
    //  a static pointer of the main window
    
        (K::SfMainWin)->close();
    }
    
    void MyWidget::onNumberChanged(int j) {
    
        txtCount->setText(QString::number(j));
    }
    ---------------------------------------------------------
    // MyObject.h
    
    #ifndef MYOBJECT_H_
    #define MYOBJECT_H_
    
    #include <QObject>
    #include <QThread>
    #include <QMutex>
    
    class MyObject : public QObject {
    
        Q_OBJECT
    
    public:
        explicit MyObject(QObject* parent = 0);
        ~MyObject();
    
        void doSetup(QThread*);
    
        int  hold;
        bool Stop;
        int  inx;
            int  lastUsedInx;
    
    signals:
        void numberChanged(int);
    
    public slots:
        void doWork();
        void pbStop_Slot();
    
    private:
        bool      isdoingwork;
        QThread*  pThread;
        QMutex    mutex;
    };
    
    #endif /* MYOBJECT_H_ */
    ----------------------------------------------------
    // MyObject.cpp    SF022
    
    #include "MyObject.h"
    
    #include <iostream>
    
    #include <QAbstractEventDispatcher>
    
    MyObject::MyObject(QObject* parent) : QObject(parent) {
    
        Stop = false;
        isdoingwork = false;
        inx = 0;
        lastUsedInx = 0;
    }
    
    MyObject::~MyObject() {
    
    }
    
    void MyObject::doSetup(QThread* thread) {
    
        pThread = thread;
        Stop = false;
        isdoingwork = false;
        connect(pThread, SIGNAL(started()), this, SLOT(doWork()));
    }
    
    void MyObject::pbStop_Slot() {
    
        mutex.lock();
        Stop = true;
        isdoingwork = false;
        mutex.unlock();
    }
    
    void MyObject::doWork() {
    
        if(isdoingwork) {
    
            return;
        }
    
        isdoingwork = true;
    
        for (inx = lastUsedInx + 1; inx < 100; inx++) {
    
            if (this->Stop) {
    
                break;
            }
    
    //  output into a text box
            emit numberChanged(inx);
    
            lastUsedInx = inx;
    
    //  process events, to allow stop to be processed using signals and slots
            (QAbstractEventDispatcher::instance(pThread))->processEvents(QEventLoop::AllEvents);
    
            QThread::msleep(800);
        }
    
        isdoingwork = false;
    }
    

    【讨论】:

      猜你喜欢
      • 2012-03-12
      • 1970-01-01
      • 1970-01-01
      • 2020-03-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-21
      • 2014-02-27
      相关资源
      最近更新 更多