qt多執行緒之 std::thread
不時見到有人會這樣做:
- 不使用QThread,而是使用pthread等平臺相關的底層 api
- 而又不想使用底層執行緒間同步、通訊的api
那麼,如何使用pthread,而又使用Qt提供的執行緒間機制呢?
本文的初衷源於此,但是使用的的是C++0x 的 std::thread,而不是直接使用unix的pthread。(既然用Qt,還是儘量保證誇平臺吧)
不想寫太多的文字,還是用一個一個的小例子來說話吧。
例子一
- 介面上一個QSpinBox
- 次執行緒中每隔一段時間會生成一個值,傳遞給該SpinBox
#include <QtGui/QApplication>
#include <QtGui/QSpinBox>
#include <thread>
void test1(QSpinBox * w)
{
for (int i=0; i<10; ++i)
{
msSleep(1000);
QMetaObject::invokeMethod(w, "setValue", Q_ARG(int, i*i));
}
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QSpinBox w;
w.show();
std::thread t1(test1, &w);
return a.exec();
}
其中 msSleep 是我們自定義的一個sleep函式:
void msSleep(int ms)
{
#ifdef Q_OS_WIN
Sleep(uint(ms));
#else
struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 };
nanosleep(&ts, NULL);
#endif
}
invokeMethod?
為什麼要用蹩腳的invokeMethod?
QMetaObject::invokeMethod(w, "setValue", Q_ARG(int, i*i));
而不是直接用
w.setValue(i*i);
Manual中說的明白:所有的GUI部件都是不可重入的!
QWidget and all its subclasses, are not reentrant. They can only be used from the main thread.
不要去挑戰它的權威!注意:在這個例子中,如果你直接用 w.setValue(i*i);,在絕大多數情況下你可能都能得到正確的結果,但是並不能證明這麼做是對的。
例子二
次執行緒到主執行緒的通訊,前面用的是invokeMethod。有無其他辦法呢?
其實在多執行緒情況下,無論是invokeMethod還是signal-slot,都是通過Qt的事件系統來完成的。
看Manual,注意Note部分:
void QCoreApplication::postEvent ( QObject * receiver, QEvent * event ) [static]
Note: This function is thread-safe.
所以,我們可以直接使用這個它(通過自定義事件來傳遞資訊):
#include <QtGui/QApplication>
#include <QtGui/QSpinBox>
#include <thread>
class Event:public QEvent
{
public:
Event(int value):QEvent(QEvent::User), value(value){}
int value;
};
class SpinBox:public QSpinBox
{
public:
SpinBox(){}
protected:
bool event(QEvent *event)
{
if (event->type() == QEvent::User){
Event * evt = static_cast<Event*>(event);
setValue(evt->value);
}
return QSpinBox::event(event);
}
};
void test1(QSpinBox * w)
{
for (int i=0; i<10; ++i)
{
msSleep(1000);
qApp->postEvent(w, new Event(i*i));
}
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
SpinBox w;
w.show();
std::thread t1(test1, &w);
return a.exec();
}
例子三
- 看一個次執行緒物件傳送訊號到主執行緒物件的例子:
#include <QtGui/QApplication>
#include <QtGui/QSpinBox>
#include <thread>
class Object:public QObject
{
Q_OBJECT
Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged)
public:
Object():m_val(0){}
int value() const{return m_val;}
public slots:
void setValue(int v)
{
if (m_val != v){
m_val = v;
emit valueChanged(v);
}
}
signals:
void valueChanged(int v);
private:
int m_val;
};
void test1(QSpinBox * w)
{
Object obj;
obj.connect(&obj, SIGNAL(valueChanged(int)), w, SLOT(setValue(int)));
for (int i=0; i<10; ++i)
{
msSleep(1000);
obj.setValue(i*i);
}
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QSpinBox w;
w.show();
std::thread t1(test1, &w);
return a.exec();
}
例子本身沒有多少可多說的。當次執行緒中Object物件的值發生變化是,會發送訊號,由於訊號connect到主執行緒的SpinBox中,所以就看到和例子一例子二完全一致的效果了。
原文:https://blog.csdn.net/dbzhang800/article/details/6592315