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中,所以就看到和例子一例子二完全一致的效果了。


你可能感兴趣的:(thread,多线程,object,qt,Signal,通讯)