详解 Qt 线程间共享数据

http://www.cnblogs.com/bingcaihuang/archive/2011/07/14/2106885.html

使用共享内存。即使用一个两个线程都能够共享的变量(如全局变量),这样两个线程都能够访问和修改该变量,从而达到共享数据的目的。

Qt 线程间共享数据是本文介绍的内容,多的不说,先来啃内容。Qt线程间共享数据主要有两种方式:

使用共享内存。即使用一个两个线程都能够共享的变量(如全局变量),这样两个线程都能够访问和修改该变量,从而达到共享数据的目的;

使用singal/slot机制,把数据从一个线程传递到另外一个线程。

第一种办法在各个编程语言都使用普遍,而第二种方式倒是QT的特有方式,下面主要学习一下这种方式:

在线程之间传递signal与在一个线程内传递signal是不一样的。在一个线程内传递signal时,emit语句会直接调用所有连接的slot并等待到所有slot被处理完;在线程之间传递signal时,slot会被放到队列中(queue),而emit这个signal后会马上返回;默认情况,线程之间使用queue机制,而线程内使用direct机制,但在connect中可以改变这些默认的机制。

view plaincopy to clipboardprint?  
//TextDevice.h    
#ifndef TEXTDEVICE_H    
#define TEXTDEVICE_H    
#include <QThread>    
#include <QString>    
#include <QMutex>     
class TextDevice : public QThread {     
    Q_OBJECT     
public:     
    TextDevice();     
    void run();     
    void stop();     
public slots:     
    void write(const QString& text);     
private:     
    int m_count;     
    QMutex m_mutex;     
};    
#endif // TEXTDEVICE_H     
    
    
//TextDevice.cpp    
#include <QMutexLocker>    
#include <QDebug>    
#include <QString>    
#include "TextDevice.h"     
TextDevice::TextDevice() {     
    m_count = 0;     
}     
void TextDevice::run() {     
    exec();     
}     
void TextDevice::stop() {     
    quit();     
}     
void TextDevice::write(const QString& text) {     
    QMutexLocker locker(&m_mutex);     
    qDebug() << QString("Call %1: %2").arg(m_count++).arg(text);     
}     
    
//TextThread.h    
#ifndef TEXTTHREAD_H    
#define TEXTTHREAD_H    
#include <QThread>    
#include <QString>     
class TextThread : public QThread {     
    Q_OBJECT     
public:     
    TextThread(const QString& text);     
    void run();     
    void stop();     
signals:     
    void writeText(const QString&);     
private:     
    QString m_text;     
    bool m_stop;     
};    
#endif // TEXTTHREAD_H     
    
//TextThread.cpp    
#include "TextThread.h"     
TextThread::TextThread(const QString& text) : QThread() {     
    m_text = text;     
    m_stop = false;     
}     
void TextThread::stop() {     
    m_stop = true;     
}     
void TextThread::run() {     
    while(!m_stop) {     
        emit writeText(m_text);     
        sleep(1);     
    }     
}     
    
//main.cpp    
#include <QApplication>    
#include <QMessageBox>    
#include "TextDevice.h"    
#include "TextThread.h"     
    
int main(int argc, char** argv) {     
    QApplication app(argc, argv);     
    //启动线程     
    TextDevice device;     
    TextThread foo("foo"), bar("bar");     
    //把两个线程使用signal/slot连接起来     
    QObject::connect(&foo, SIGNAL(writeText(const QString&)), &device, SLOT(write(const QString&)));     
    QObject::connect(&bar, SIGNAL(writeText(const QString&)), &device, SLOT(write(const QString&)));     
    //启动线程     
    foo.start();     
    bar.start();     
    device.start();     
    QMessageBox::information(0, "Threading", "Close me to stop.");     
    //停止线程     
    foo.stop();     
    bar.stop();     
    device.stop();     
    //等待线程结束     
    device.wait();     
    foo.wait();     
    bar.wait();     
    return 0;     
}    
//TextDevice.h  
#ifndef TEXTDEVICE_H  
#define TEXTDEVICE_H  
#include <QThread> 
#include <QString> 
#include <QMutex> 
class TextDevice : public QThread {  
    Q_OBJECT  
public:  
    TextDevice();  
    void run();  
    void stop();  
public slots:  
    void write(const QString& text);  
private:  
    int m_count;  
    QMutex m_mutex;  
};  
#endif // TEXTDEVICE_H  
 
 
//TextDevice.cpp  
#include <QMutexLocker> 
#include <QDebug> 
#include <QString> 
#include "TextDevice.h"  
TextDevice::TextDevice() {  
    m_count = 0;  
}  
void TextDevice::run() {  
    exec();  
}  
void TextDevice::stop() {  
    quit();  
}  
void TextDevice::write(const QString& text) {  
    QMutexLocker locker(&m_mutex);  
    qDebug() << QString("Call %1: %2").arg(m_count++).arg(text);  
}  
 
//TextThread.h  
#ifndef TEXTTHREAD_H  
#define TEXTTHREAD_H  
#include <QThread> 
#include <QString> 
class TextThread : public QThread {  
    Q_OBJECT  
public:  
    TextThread(const QString& text);  
    void run();  
    void stop();  
signals:  
    void writeText(const QString&);  
private:  
    QString m_text;  
    bool m_stop;  
};  
#endif // TEXTTHREAD_H  
 
//TextThread.cpp  
#include "TextThread.h"  
TextThread::TextThread(const QString& text) : QThread() {  
    m_text = text;  
    m_stop = false;  
}  
void TextThread::stop() {  
    m_stop = true;  
}  
void TextThread::run() {  
    while(!m_stop) {  
        emit writeText(m_text);  
        sleep(1);  
    }  
}  
 
//main.cpp  
#include <QApplication> 
#include <QMessageBox> 
#include "TextDevice.h"  
#include "TextThread.h"  
int main(int argc, char** argv) {  
    QApplication app(argc, argv);  
    //启动线程  
    TextDevice device;  
    TextThread foo("foo"), bar("bar");  
    //把两个线程使用signal/slot连接起来  
    QObject::connect(&foo, SIGNAL(writeText(const QString&)), &device, SLOT(write(const QString&)));  
    QObject::connect(&bar, SIGNAL(writeText(const QString&)), &device, SLOT(write(const QString&)));  
    //启动线程  
    foo.start();  
    bar.start();  
    device.start();  
    QMessageBox::information(0, "Threading", "Close me to stop.");  
    //停止线程  
    foo.stop();  
    bar.stop();  
    device.stop();  
    //等待线程结束  
    device.wait();  
    foo.wait();  
    bar.wait();  
    return 0;  
}

上面例子代码可以看出两个线程之间传送了类型为QString的信息。像QString等这些QT本身定义的类型,直接传送即可。但如果是自己定义的类型如果想使用signal/slot来传递的话,则没有这么简单。直接使用的话,会产生下面这种错误:

1 QObject::connect: Cannot queue arguments of type 'TextAndNumber' (Make sure 'TextAndNumber' is registed using qRegisterMetaType().) 

原因:当一个signal被放到队列中(queued)时,它的参数(arguments)也会被一起一起放到队列中(queued起来),这就意味着参数在被传送到slot之前需要被拷贝、存储在队列中(queue)中;为了能够在队列中存储这些参数(argument),Qt需要去construct、destruct、copy这些对象,而为了让Qt知道怎样去作这些事情,参数的类型需要使用qRegisterMetaType来注册(如错误提示中的说明)

步骤:(以自定义TextAndNumber类型为例)

自定一种类型,在这个类型的顶部包含:#include <QMetaType>

在类型定义完成后,加入声明:Q_DECLARE_METATYPE(TextAndNumber);

在main()函数中注册这种类型:qRegisterMetaType<TextAndNumber>("TextAndNumber");

如果还希望使用这种类型的引用,可同样要注册:qRegisterMetaType<TextAndNumber>("TextAndNumber&");

原因:当一个signal被放到队列中(queued)时,它的参数(arguments)也会被一起一起放到队列中(queued起来),这就意味着参数在被传送到slot之前需要被拷贝、存储在队列中(queue)中;为了能够在队列中存储这些参数(argument),Qt需要去construct、destruct、copy这些对象,而为了让Qt知道怎样去作这些事情,参数的类型需要使用qRegisterMetaType来注册(如错误提示中的说明)

步骤:(以自定义TextAndNumber类型为例)

自定一种类型,在这个类型的顶部包含:#include <QMetaType>

在类型定义完成后,加入声明:Q_DECLARE_METATYPE(TextAndNumber);

在main()函数中注册这种类型:qRegisterMetaType<TextAndNumber>("TextAndNumber");

如果还希望使用这种类型的引用,可同样要注册:qRegisterMetaType<TextAndNumber>("TextAndNumber&");

view plaincopy to clipboardprint?  
//TextAndNumber.h     
#ifndef TEXTANDNUMBER_H     
#define TEXTANDNUMBER_H     
#include <QMetaType>     
//必须包含QMetaType,否则会出现下面错误:     
//error: expected constructor, destructor, or type conversion before ‘;’ token     
#include <QString>     
class TextAndNumber {     
public:     
    TextAndNumber();     
    TextAndNumber(int, QString);     
    int count();     
    QString text();     
private:     
    int m_count;     
    QString m_text;     
};     
Q_DECLARE_METATYPE(TextAndNumber);     
#endif // TEXTANDNUMBER_H     
    
//TextAndNumber.cpp     
#include "TextAndNumber.h"     
TextAndNumber::TextAndNumber() {     
}     
TextAndNumber::TextAndNumber(int count, QString text) {     
    m_count = count;     
    m_text = text;     
}     
int TextAndNumber::count() {     
    return m_count;     
}     
QString TextAndNumber::text() {     
    return m_text;     
}     
    
//TextDevice.h     
#ifndef TEXTDEVICE_H     
#define TEXTDEVICE_H     
#include <QThread>     
#include <QDebug>     
#include <QString>     
#include "TextAndNumber.h"     
class TextDevice : public QThread {     
    Q_OBJECT     
public:     
    TextDevice();     
    void run();     
    void stop();     
public slots:     
    void write(TextAndNumber& tran);     
private:     
    int m_count;     
};     
#endif // TEXTDEVICE_H     
    
//TextDevice.cpp     
#include "TextDevice.h"     
TextDevice::TextDevice() : QThread() {     
    m_count = 0;     
}     
void TextDevice::run() {     
    exec();     
}     
void TextDevice::stop() {     
    quit();     
}     
void TextDevice::write(TextAndNumber& tran) {     
    qDebug() << QString("Call %1 (%3): %2").arg(m_count++).arg(tran.text()).arg(tran.count());     
}     
    
//TextThread.h     
#ifndef TEXTTHREAD_H     
#define TEXTTHREAD_H     
#include <QThread>     
#include <QString>     
#include "TextAndNumber.h"     
class TextThread : public QThread {     
    Q_OBJECT     
public:     
    TextThread(const QString& text);     
    void run();     
    void stop();     
signals:     
    void writeText(TextAndNumber& tran);     
private:     
    QString m_text;     
    int m_count;     
    bool m_stop;     
};     
    
#endif // TEXTTHREAD_H     
    
//TextThread.cpp     
#include "TextThread.h"     
TextThread::TextThread(const QString& text) : QThread() {     
    m_text = text;     
    m_stop = false;     
    m_count = 0;     
}     
void TextThread::run() {     
    while(!m_stop) {     
        TextAndNumber tn(m_count++, m_text);     
        emit writeText(tn);     
        sleep(1);     
    }     
}     
void TextThread::stop() {     
    m_stop = true;     
}     
    
//main.cpp     
#include <QApplication>     
#include <QMessageBox>     
#include "TextThread.h"     
#include "TextDevice.h"     
#include "TextAndNumber.h"     
int main(int argc, char *argv[])     
{     
    QApplication app(argc, argv);     
    qRegisterMetaType<TextAndNumber>("TextAndNumber");     
    qRegisterMetaType<TextAndNumber>("TextAndNumber&");     
    TextDevice device;     
    TextThread foo("foo"), bar("bar");     
    QObject::connect(&foo, SIGNAL(writeText(TextAndNumber&)), &device, SLOT(write(TextAndNumber&)));     
    QObject::connect(&bar, SIGNAL(writeText(TextAndNumber&)), &device, SLOT(write(TextAndNumber&)));     
    device.start();     
    foo.start();     
    bar.start();     
    QMessageBox::information(0, "Threading", "Click me to close");     
    foo.stop();     
    bar.stop();     
    device.stop();     
    foo.wait();     
    bar.wait();     
    device.wait();     
    qDebug() << "Application end.";     
    return 0;     
}



  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值