参考:http://www.cnblogs.com/bingcaihuang/archive/2011/07/14/2106885.html
Qt线程间共享数据主要有两种方式:一是使用共享内存。即使用一个两个线程都能够共享的变量(如全局变量),这样两个线程都能够访问和修改该变量,从而达到共享数据的目的;二是使用singal/slot机制,把数据从一个线程传递到另外一个线程。
第一种办法在各个编程语言都使用普遍,而第二种方式倒是QT的特有方式,下面主要学习一下这种方式:在线程之间传递signal与在一个线程内传递signal是不一样的。在一个线程内传递signal时,emit语句会直接调用所有连接的slot并等待到所有slot被处理完;在线程之间传递signal时,slot会被放到队列中(queue),而emit这个signal后会马上返回;默认情况,线程之间使用queue机制,而线程内使用direct机制,但在connect中可以改变这些默认的机制。
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来传递的话,则没有这么简单。直接使用的话,会产生下面这种错误:
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&");
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;
-
}