不时见到有人会这样做:
- 不使用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