Qt学习:线程间共享数据

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

 

 

 

你可能感兴趣的:(application,存储,qt,Constructor,Signal,destructor)