2015-01-26 67 views
1

使用该程序我按下“运行”按钮和用于循环周期100次(具有100毫秒的延迟) 并打印在一个txt字段做的QThread-ING“正确”的方式

我循环计数已经成功完成了从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); 

    } 
} 

+0

看一看以下链接一些见解:https://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/ – 2015-01-26 20:23:27

+0

如果这只是一个练习,那么确定,但通常你会用'QTimer'来做这样的事情,而不需要工作线程。阅读[this](http://qt-project.org/wiki/Threads_Events_QObjects#c2e33c61ced29e1eb1bee9a1671f202e)了解更多信息。 – thuga 2015-01-27 08:39:34

回答

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作为100毫秒的心跳,而不是让线程进入睡眠状态。但不改变太多你的代码,你可以使用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。现在,如果用户按下run,同时执行蹲位它将在其内部调用。你现在有一个令人讨厌的代码。一个简单的方法来解决这个问题,就是把一个布尔值设置在dowork的开头。

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

这是实现reentrancy的穷人的方式。您将在Qt文档中经常看到可重入字。

祝你好运在你的多线程之旅。

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