1. 程式人生 > >qt多執行緒之 std::thread

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