深入研究Qt多线程

参考资料

  1. http://blog.debao.me/2013/08/how-to-use-qthread-in-the-right-way-part-1/
  2. http://blog.debao.me/2013/08/how-to-use-qthread-in-the-right-way-part-2/

说明:在多线程程序中,线程分为主线程(main thread)和工作线程(worker thread)

run() 函数是QThread 类的入口

#include 

class Thread : public QThread
{
private:
    void run()
    {
        qDebug()<<"From worker thread: "<();
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    qDebug()<<"From main thread: "<<QThread::currentThreadId();

    Thread t;
    QObject::connect(&t, SIGNAL(finished()), &a, SLOT(quit()));

    t.start();
    return a.exec();
}

输出结果大致如下:

From main thread:  0x15a8 
From worker thread:  0x128c

只有在run()函数中实例化的对象,在调用其成员函数时,才会在worker thread中运行

  • 例外:可以使用moveToThread修改对象所属的线程
  • 像下面的这个例子:
  • 1、信号发出者在主线程中定义
  • 2、信号&槽在主线程中进行关联
  • 3、被关联的槽函数是工作线程类的成员函数(仅这一条就足以得出不能在工作线程中运行的结论了。因为调用线程类的成员函数,相当于线程对象调用其自身成员函数,而线程对象必然是在主线程中实例化的。此处不考虑在工作线程中创建另一个工作线程的情况)
  • 4、结果自然是,槽函数没有在工作线程中执行
  • 5、原因:因为工作线程类的对象必须在主线程中实例化。一个对象在哪个线程中实例化的,它的成员函数就在哪个线程中执行。除非使用moveToThread。
#if QT_VERSION>=0x050000
#include 
#else
#include 
#endif

class Thread : public QThread
{
    Q_OBJECT

public:
    Thread():m_stop(false)
    {}

public slots:
    void stop()
    {
        qDebug()<<"Thread::stop called from main thread: "<true;
    }

private:
    QMutex m_mutex;
    bool m_stop;

    void run()
    {
        qDebug()<<"From worker thread: "<while (1) {
            {
            QMutexLocker locker(&m_mutex);
            if (m_stop) break;
            }
            msleep(10);
        }
    }
};

#include "main.moc"
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    qDebug()<<"From main thread: "<"Stop Thread");
    Thread t;

    QObject::connect(&btn, SIGNAL(clicked()), &t, SLOT(stop()));
    QObject::connect(&t, SIGNAL(finished()), &a, SLOT(quit()));

    t.start();
    btn.show();
    return a.exec();
}

输出结果大致如下:

From main thread:  0x13a8 
From worker thread:  0xab8 
Thread::stop called from main thread:  0x13a8
  • 再看下一个例子:
  • 1、定时器在run()函数中定义
  • 2、定时器的信号与槽函数在run()函数中进行关联
  • 3、槽函数是工作线程类的成员函数
  • 4、运行结果是,槽函数没有在工作线程中执行。这并不意外,只要是工作线程类的成员函数,必然是被工作线程类的实例所调用,而该实例必然在主线程中实例化,因此,工作线程类的成员函数必然是在主线程中执行。
#include 

class Thread : public QThread
{
    Q_OBJECT
private slots:
    void onTimeout()
    {
        qDebug()<<"Thread::onTimeout get called from? : "<private:
    void run()
    {
        qDebug()<<"From worker thread: "<this, SLOT(onTimeout()));
        timer.start(1000);

        exec();
    }
};

#include "main.moc"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    qDebug()<<"From main thread: "<return a.exec();
}

输出结果大致如下:

From main thread:  0x13a4 
From worker thread:  0x1330 
Thread::onTimeout get called from?:  0x13a4 
Thread::onTimeout get called from?:  0x13a4 
Thread::onTimeout get called from?:  0x13a4

正确示例

  • QTimer 在run()函数中实例化
  • Worker 在run()函数中实例化
  • 信号&槽在run()函数中关联(这一步不是必要的,比如,可以把以上两个类的指针在线程类的头文件中声明,在run()函数中实例化,这样信号&槽就可以在线程类的构造函数或其他成员函数中进行关联,运行结果是一样的)

class Worker : public QObject
{
    Q_OBJECT
private slots:
    void onTimeout()
    {
        qDebug()<<"Worker::onTimeout get called from?: "<public QThread
{
    Q_OBJECT

private:
    void run()
    {
        qDebug()<<"From work thread: "<1000);

        exec();
    }
};

#include "main.moc"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    qDebug()<<"From main thread: "<return a.exec();
}

运行结果如下:

From main thread:  0x810 
From work thread:  0xfac 
Worker::onTimeout get called from?:  0xfac 
Worker::onTimeout get called from?:  0xfac 
Worker::onTimeout get called from?:  0xfac

moveToThread 的使用

  • 信号&槽在同一个线程中,它们的连接方式是direct connection
  • 信号&槽在同一个线程中,它们的连接方式是queued connection
  • queued connections的优势在于,这样的连接是线程安全的,我们不用使用QMutex 这样的类进行认为控制了
#include 

class Worker : public QObject
{
    Q_OBJECT
private slots:
    void onTimeout()
    {
        qDebug()<<"Worker::onTimeout get called from?: "<<QThread::currentThreadId();
    }
};

#include "main.moc"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    qDebug()<<"From main thread: "<<QThread::currentThreadId();

    QThread t;
    QTimer timer;
    Worker worker;

    QObject::connect(&timer, SIGNAL(timeout()), &worker, SLOT(onTimeout()));
    timer.start(1000);

    timer.moveToThread(&t);//这句代码不是必要的,不影响运行结果
    worker.moveToThread(&t);

    t.start();

    return a.exec();
}

运行结果如下:

From main thread:  0x1310 
Worker::onTimeout get called from?:  0x121c 
Worker::onTimeout get called from?:  0x121c 
Worker::onTimeout get called from?:  0x121c

线程关联度(Thread Affinity)

  • 当我们发送一个event时,响应这个event的函数在哪个线程中执行?响应该enent事件的函数所调用的函数又在哪个线程中执行呢?其源码如下:
void QCoreApplication::postEvent(QObject *receiver, QEvent *event)
{
    QThreadData * volatile * pdata = &receiver->d_func()->threadData;
    QThreadData *data = *pdata;
    QMutexLocker locker(&data->postEventList.mutex);
    data->postEventList.addEvent(QPostEvent(receiver, event));
}

实例如下:

#if QT_VERSION>=0x050000
#include 
#else
#include 
#endif

class Test : public QObject
{
    Q_OBJECT
protected:
    bool event(QEvent *evt)
    {
        if (evt->type() == QEvent::User) {
            qDebug()<<"Event received in thread"<return true;
        }
        return QObject::event(evt);
    }
};

class Button : public QPushButton
{
    Q_OBJECT
    Test *m_test;
public:
    Button(Test *test):QPushButton("Send Event"), m_test(test)
    {
        connect(this, SIGNAL(clicked()), SLOT(onClicked()));
    }

private slots:
    void onClicked()
    {
        QCoreApplication::postEvent(m_test, new QEvent(QEvent::User));
    }
};

#include "main.moc"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    qDebug()<<"From main thread: "<return a.exec();
}

运行结果如下:

From main thread:  QThread(0x9e8100) 
Event received in thread QThread(0x9e8100) 
Event received in thread QThread(0x9e8100)

添加以下三句代码,结果就会不同

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    qDebug()<<"From main thread: "<//new line
    test.moveToThread(&thread);   //new line
    thread.start();               //new line

    Button btn(&test);
    btn.show();

    return a.exec();
}

运行结果如下:

From main thread:  QThread(0x9e8100) 
Event received in thread QThread(0x13fed4) 
Event received in thread QThread(0x13fed4)
  • 以上的例子再次说明,无论函数之间经过怎样错综复杂的调用或者类之间经过了怎样的嵌套,响应event事件的类的对象属于哪个线程,响应event事件的函数就在哪个线程中执行。
  • 也就是说,对象在哪个线程,该对象调用它自身的成员函数就在哪个线程中执行

你可能感兴趣的:(Qt)