1. 程式人生 > >詳解 Qt 執行緒間共享資料(使用signal/slot傳遞資料,執行緒間傳遞訊號會立刻返回,但也可通過connect改變)

詳解 Qt 執行緒間共享資料(使用signal/slot傳遞資料,執行緒間傳遞訊號會立刻返回,但也可通過connect改變)

相關文章

使用共享記憶體。即使用一個兩個執行緒都能夠共享的變數(如全域性變數),這樣兩個執行緒都能夠訪問和修改該變數,從而達到共享資料的目的。

Qt 執行緒間共享資料是本文介紹的內容,多的不說,先來啃內容。Qt執行緒間共享資料主要有兩種方式:

使用共享記憶體。即使用一個兩個執行緒都能夠共享的變數(如全域性變數),這樣兩個執行緒都能夠訪問和修改該變數,從而達到共享資料的目的;

使用singal/slot機制,把資料從一個執行緒傳遞到另外一個執行緒。

第一種辦法在各個程式語言都使用普遍,而第二種方式倒是QT的特有方式,下面主要學習一下這種方式:

線上程之間傳遞signal與在一個執行緒內傳遞signal是不一樣的。在一個執行緒內傳遞signal時,emit語句會直接呼叫所有連線的slot並等待到所有slot被處理完;線上程之間傳遞signal時,slot會被放到佇列中(queue),而emit這個signal後會馬上返回;預設情況,執行緒之間使用queue機制,而執行緒內使用direct機制,但在connect中可以改變這些預設的機制。

複製程式碼複製程式碼
  1 view plaincopy to clipboardprint?  
2 //TextDevice.h
3 #ifndef TEXTDEVICE_H
4 #define TEXTDEVICE_H
5 #include <QThread>
6 #include <QString>
7 #include <QMutex>
8 class TextDevice : public QThread {
9 Q_OBJECT
10 public:
11 TextDevice();
12 void run();
13 void stop();
14 public slots:
15 void write(const QString& text);
16 private:
17 int m_count;
18 QMutex m_mutex;
19 };
20 #endif // TEXTDEVICE_H
21
22
23 //TextDevice.cpp
24 #include <QMutexLocker>
25 #include <QDebug>
26 #include <QString>
27 #include "TextDevice.h"
28 TextDevice::TextDevice() {
29 m_count = 0;
30 }
31 void TextDevice::run() {
32 exec();
33 }
34 void TextDevice::stop() {
35 quit();
36 }
37 void TextDevice::write(const QString& text) {
38 QMutexLocker locker(&m_mutex);
39 qDebug() << QString("Call %1: %2").arg(m_count++).arg(text);
40 }
41
42 //TextThread.h
43 #ifndef TEXTTHREAD_H
44 #define TEXTTHREAD_H
45 #include <QThread>
46 #include <QString>
47 class TextThread : public QThread {
48 Q_OBJECT
49 public:
50 TextThread(const QString& text);
51 void run();
52 void stop();
53 signals:
54 void writeText(const QString&);
55 private:
56 QString m_text;
57 bool m_stop;
58 };
59 #endif // TEXTTHREAD_H
60
61 //TextThread.cpp
62 #include "TextThread.h"
63 TextThread::TextThread(const QString& text) : QThread() {
64 m_text = text;
65 m_stop = false;
66 }
67 void TextThread::stop() {
68 m_stop = true;
69 }
70 void TextThread::run() {
71 while(!m_stop) {
72 emit writeText(m_text);
73 sleep(1);
74 }
75 }
76
77 //main.cpp
78 #include <QApplication>
79 #include <QMessageBox>
80 #include "TextDevice.h"
81 #include "TextThread.h"
82
83 int main(int argc, char** argv) {
84 QApplication app(argc, argv);
85 //啟動執行緒
86 TextDevice device;
87 TextThread foo("foo"), bar("bar");
88 //把兩個執行緒使用signal/slot連線起來
89 QObject::connect(&foo, SIGNAL(writeText(const QString&)), &device, SLOT(write(const QString&)));
90 QObject::connect(&bar, SIGNAL(writeText(const QString&)), &device, SLOT(write(const QString&)));
91 //啟動執行緒
92 foo.start();
93 bar.start();
94 device.start();
95 QMessageBox::information(0, "Threading", "Close me to stop.");
96 //停止執行緒
97 foo.stop();
98 bar.stop();
99 device.stop();
100 //等待執行緒結束
101 device.wait();
102 foo.wait();
103 bar.wait();
104 return 0;
105 }
106 //TextDevice.h
107 #ifndef TEXTDEVICE_H
108 #define TEXTDEVICE_H
109 #include <QThread>
110 #include <QString>
111 #include <QMutex>
112 class TextDevice : public QThread {
113 Q_OBJECT
114 public:
115 TextDevice();
116 void run();
117 void stop();
118 public slots:
119 void write(const QString& text);
120 private:
121 int m_count;
122 QMutex m_mutex;
123 };
124 #endif // TEXTDEVICE_H
125
126
127 //TextDevice.cpp
128 #include <QMutexLocker>
129 #include <QDebug>
130 #include <QString>
131 #include "TextDevice.h"
132 TextDevice::TextDevice() {
133 m_count = 0;
134 }
135 void TextDevice::run() {
136 exec();
137 }
138 void TextDevice::stop() {
139 quit();
140 }
141 void TextDevice::write(const QString& text) {
142 QMutexLocker locker(&m_mutex);
143 qDebug() << QString("Call %1: %2").arg(m_count++).arg(text);
144 }
145
146 //TextThread.h
147 #ifndef TEXTTHREAD_H
148 #define TEXTTHREAD_H
149 #include <QThread>
150 #include <QString>
151 class TextThread : public QThread {
152 Q_OBJECT
153 public:
154 TextThread(const QString& text);
155 void run();
156 void stop();
157 signals:
158 void writeText(const QString&);
159 private:
160 QString m_text;
161 bool m_stop;
162 };
163 #endif // TEXTTHREAD_H
164
165 //TextThread.cpp
166 #include "TextThread.h"
167 TextThread::TextThread(const QString& text) : QThread() {
168 m_text = text;
169 m_stop = false;
170 }
171 void TextThread::stop() {
172 m_stop = true;
173 }
174 void TextThread::run() {
175 while(!m_stop) {
176 emit writeText(m_text);
177 sleep(1);
178 }
179 }
180
181 //main.cpp
182 #include <QApplication>
183 #include <QMessageBox>
184 #include "TextDevice.h"
185 #include "TextThread.h"
186 int main(int argc, char** argv) {
187 QApplication app(argc, argv);
188 //啟動執行緒
189 TextDevice device;
190 TextThread foo("foo"), bar("bar");
191 //把兩個執行緒使用signal/slot連線起來
192 QObject::connect(&foo, SIGNAL(writeText(const QString&)), &device, SLOT(write(const QString&)));
193 QObject::connect(&bar, SIGNAL(writeText(const QString&)), &device, SLOT(write(const QString&)));
194 //啟動執行緒
195 foo.start();
196 bar.start();
197 device.start();
198 QMessageBox::information(0, "Threading", "Close me to stop.");
199 //停止執行緒
200 foo.stop();
201 bar.stop();
202 device.stop();
203 //等待執行緒結束
204 device.wait();
205 foo.wait();
206 bar.wait();
207 return 0;
208 }
複製程式碼複製程式碼

  上面例子程式碼可以看出兩個執行緒之間傳送了型別為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&");

複製程式碼複製程式碼
  1 view plaincopy to clipboardprint?  
2 //TextAndNumber.h
3 #ifndef TEXTANDNUMBER_H
4 #define TEXTANDNUMBER_H
5 #include <QMetaType>
6 //必須包含QMetaType,否則會出現下面錯誤:
7 //error: expected constructor, destructor, or type conversion before ‘;’ token
8 #include <QString>
9 class TextAndNumber {
10 public:
11 TextAndNumber();
12 TextAndNumber(int, QString);
13 int count();
14 QString text();
15 private:
16 int m_count;
17 QString m_text;
18 };
19 Q_DECLARE_METATYPE(TextAndNumber);
20 #endif // TEXTANDNUMBER_H
21
22 //TextAndNumber.cpp
23 #include "TextAndNumber.h"
24 TextAndNumber::TextAndNumber() {
25 }
26 TextAndNumber::TextAndNumber(int count, QString text) {
27 m_count = count;
28 m_text = text;
29 }
30 int TextAndNumber::count() {
31 return m_count;
32 }
33 QString TextAndNumber::text() {
34 return m_text;
35 }
36
37 //TextDevice.h
38 #ifndef TEXTDEVICE_H
39 #define TEXTDEVICE_H
40 #include <QThread>
41 #include <QDebug>
42 #include <QString>
43 #include "TextAndNumber.h"
44 class TextDevice : public QThread {
45 Q_OBJECT
46 public:
47 TextDevice();
48 void run();
49 void stop();
50 public slots:
51 void write(TextAndNumber& tran);
52 private:
53 int m_count;
54 };
55 #endif // TEXTDEVICE_H
56
57 //TextDevice.cpp
58 #include "TextDevice.h"
59 TextDevice::TextDevice() : QThread() {
60 m_count = 0;
61 }
62 void TextDevice::run() {
63 exec();
64 }
65 void TextDevice::stop() {
66 quit();
67 }
68 void TextDevice::write(TextAndNumber& tran) {
69 qDebug() << QString("Call %1 (%3): %2").arg(m_count++).arg(tran.text()).arg(tran.count());
70 }
71
72 //TextThread.h
73 #ifndef TEXTTHREAD_H
74 #define TEXTTHREAD_H
75 #include <QThread>
76 #include <QString>
77 #include "TextAndNumber.h"
78 class TextThread : public QThread {
79 Q_OBJECT
80 public:
81 TextThread(const QString& text);
82 void run();
83 void stop();
84 signals:
85 void writeText(TextAndNumber& tran);
86 private:
87 QString m_text;
88 int m_count;
89 bool m_stop;
90 };
91
92 #endif // TEXTTHREAD_H
93
94 //TextThread.cpp
95 #include "TextThread.h"
96 TextThread::TextThread(const QString& text) : QThread() {
97 m_text = text;
98 m_stop = false;
99 m_count = 0;
100 }
101 void TextThread::run() {
102 while(!m_stop) {
103 TextAndNumber tn(m_count++, m_text);
104 emit writeText(tn);
105 sleep(1);
106 }
107 }
108 void TextThread::stop() {
109 m_stop = true;
110 }
111
112 //main.cpp
113 #include <QApplication>
114 #include <QMessageBox>
115 #include "TextThread.h"
116 #include "TextDevice.h"
117 #include "TextAndNumber.h"
118 int main(int argc, char *argv[])
119 {
120 QApplication app(argc, argv);
121 qRegisterMetaType<TextAndNumber>("TextAndNumber");
122 qRegisterMetaType<TextAndNumber>("TextAndNumber&");
123 TextDevice device;
124 TextThread foo("foo"), bar("bar");
125 QObject::connect(&foo, SIGNAL(writeText(TextAndNumber&)), &device, SLOT(write(TextAndNumber&)));
126 QObject::connect(&bar, SIGNAL(writeText(TextAndNumber&)), &device, SLOT(write(TextAndNumber&)));
127 device.start();
128 foo.start();
129 bar.start();
130 QMessageBox::information(0, "Threading", "Click me to close");
131 foo.stop();
132 bar.stop();
133 device.stop();
134 foo.wait();
135 bar.wait();
136 device.wait();
137 qDebug() << "Application end.";
138 return 0;
139 }
複製程式碼複製程式碼

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