以正确的方式做QThread

时间:2015-01-26 19:01:08

标签: c++ multithreading qt qthread

通过这个程序,我按下'运行'按钮和for循环循环100次(延迟100ms) 并在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);

    }
}

2 个答案:

答案 0 :(得分:1)

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

myObject = new MyObject(this);

将QObject移动到另一个线程he should not have a parent。如果它确实Qt将 静默 告诉你出错(通过在输出上打印,同样连接不正确)。框架设计不要对此类警告感到恐慌......

应该是

myObject = new MyObject(0);

现在已经清除,代码中还有其他缺陷。

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

    MyObject::pbStop_Slot()
    {
        mutex.lock()
        Stop = true;
        mutex.unlock()
    }
    
  4. 现在你的程序在技术上应该是正确的。但很糟糕,你不能使用信号和插槽,因为你的线程被阻止执行doWork。此外,我们可以做一些锁定。事实上,是的。我将采用的方式是使用Qtimer作为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);    
    }
    

    }

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

    dowork(){
      if(isdoingwork)
        return;
      isdoingwork = true
      for(...
    
  6. 这是实现reentrancy的穷人方式。在Qt文档中,您会经常看到reentrant这个词。

    祝你多线程之旅好运。

答案 1 :(得分:0)

非常好的UmNyobe!

作为参考,我在这里添加了更正后的代码。

// 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;
}