1. 对象的依附性
(1)默认情况下,对象依附于自身被创建的线程;例如,对象在主线程创建,则依附于主线程。
int main(int argc, char* argv[]) { //... MyThread t; /* t依附于主线程*/ MyObject m; /* m依附于主线程*/ //... }
(2)对象的依附性与槽函数执行的关系
①如果采用Qt:: DirectConnection时(即直接连接),表示直接让发射信号的线程去执行槽函数。这时无需关心接收者的依附性。
②如果采用Qt::QueuedConnection时(即队列连接),信号将被投递到接收者所依附线程,所以槽函数由接收者依附的线程执行。
③默认连接(Qt::AutoConnection):发送和接收同一线程,采用直接连接,槽函数由发送线程执行,当然也就等于接收者线程在执行,所以可以看接收者的依附性。如果不同线程间,会采用队列发送,信号须发送到接收者线程,所以仍然可以看接收者的依附性。(注意,同线程之间也可显式指定为队列连接,不同线程间同样可以显式指定为直接连接;只是默认情况下,同线程间采用的是直接连接,不同线程间采用的是队列连接)
④综上,如果指定为Qt:: DirectConnection,则槽函数由发送者线程执行。除此之外其他情况(含Auto和queue连接),槽函数均由接收者所依附线程执行
(3)对象的依附性是可以改变的:QObject::moveToThread用于改变线程的依附性。
【编验实验】对象的依附性
//MyObject.h
#ifndef MYOBJECT_H #define MYOBJECT_H #include <QObject> class MyObject : public QObject { Q_OBJECT public: explicit MyObject(QObject* parent = 0); protected slots: void getStarted(); void testSlot(); }; #endif // MYOBJECT_H
//MyObject.cpp
#include "MyObject.h" #include <QThread> #include <QDebug> MyObject::MyObject(QObject *parent):QObject(parent) { } void MyObject::getStarted() { qDebug()<<"void MyObject::getStarted() tid = " << QThread::currentThreadId(); } void MyObject::testSlot() { qDebug()<<"MyObject::testSlot() tid = " << QThread::currentThreadId(); }
//TestThread.h
#ifndef TESTTHREAD_H #define TESTTHREAD_H #include <QThread> class TestThread : public QThread { Q_OBJECT protected: void run(); public: explicit TestThread(QObject* parent = 0); signals: void testSignal(); protected slots: void testSlot(); }; #endif // TESTTHREAD_H
//TestThread.cpp
#include "TestThread.h" #include <QDebug> TestThread::TestThread(QObject *parent): QThread(parent) { //由run中的emit testSignal可看出,子线程发出信号,接收信号由t对象完成 //而在本例中t对象的可能分别依附于: //(1)测试1中:t对象依附于主线程。两者属不同的线程,所以采用队列连接,信号发往主线程 //的事件队列,槽函数由主线程处理。 //(2)测试2中:t对象依附于子线程,两者属同一线程,采用直接连接,槽函数由子线程自己处理。 connect(this, SIGNAL(testSignal()), this, SLOT(testSlot()));//默认连接,也可由t的依附性 //看出槽函数的执行线程 } void TestThread::run() { qDebug() << "void TestThread::run() —— begin tid = " << currentThreadId(); for(int i=0; i<5; i++){ qDebug() << "void TestThread::run() i = " << i; sleep(1); } emit testSignal(); //子线程发出信号 qDebug() << "void TestThread::run() —— end"; } void TestThread::testSlot() { qDebug() << "void TestThread::testSlot() tid = " << currentThreadId(); }
//main.cpp
#include <QCoreApplication> #include <QThread> #include <QDebug> #include <QMutex> #include "TestThread.h" #include "MyObject.h" //自定义消息处理程序,让QDebug输出是线程安全的。 void messageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) { QByteArray localMsg = msg.toLocal8Bit(); //fcntl(stderr, ); static QMutex mutex; mutex.lock(); switch (type) { case QtDebugMsg: fprintf(stderr, "%s\n", localMsg.constData()); break; case QtInfoMsg: fprintf(stderr, "Info: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); break; case QtWarningMsg: fprintf(stderr, "Warning: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); break; case QtCriticalMsg: fprintf(stderr, "Critical: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); break; case QtFatalMsg: fprintf(stderr, "Fatal: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); abort(); } mutex.unlock(); } int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); // 安装消息处理程序 qInstallMessageHandler(messageOutput); qDebug() << "main() tid = " << QThread::currentThreadId(); TestThread t; MyObject m; /* //测试1 //started和testSignal信号都是由子线程发出,m对象(依附于主线程)接收信号。因两者属不同线程 //默认会采用队列连接,信号被投递到m对象所依附的主线程,即两个槽函数都将在主线程中执行! QObject::connect(&t, SIGNAL(started()), &m, SLOT(getStarted())); //默认连接,也可按m的依附性判断执行于主线程 QObject::connect(&t, SIGNAL(testSignal()), &m, SLOT(testSlot()));//同上 t.start(); */ //测试2 //子线程发出started,同时本例中testSignal信号也是由子线程发出的,m对象(依附于子线程)接收信号。因收发属同一个线程 //默认采用直接连接,即槽函数在子线程中执行! QObject::connect(&t, SIGNAL(started()), &m, SLOT(getStarted())); //默认连接,也可按m的依附性判断执行于子线程 QObject::connect(&t, SIGNAL(testSignal()), &m, SLOT(testSlot()));//同上 m.moveToThread(&t); //改变m的依附性为子线程 //t.moveToThread(&t); //会影响TestThread::testSlot()的执行线程 t.start(); return a.exec(); } /*输出结果: 测试1: main() tid = 0x1fcc //主线程 void TestThread::run() —— begin tid = 0x278c //子线程 void MyObject::getStarted() tid = 0x1fcc //m依附于主线程 void TestThread::run() i = 0 void TestThread::run() i = 1 void TestThread::run() i = 2 void TestThread::run() i = 3 void TestThread::run() i = 4 void TestThread::testSlot() tid = 0x1fcc //t依附于主线程 void TestThread::run() —— end MyObject::testSlot() tid = 0x1fcc //m依附于主线程 测试2 main() tid = 0x95c //主线程 void MyObject::getStarted() tid = 0x5c //m依附于子线程 void TestThread::run() —— begin tid = 0x5c void TestThread::run() i = 0 void TestThread::run() i = 1 void TestThread::run() i = 2 void TestThread::run() i = 3 void TestThread::run() i = 4 MyObject::testSlot() tid = 0x5c //m依附于子线程 void TestThread::testSlot() tid = 0x95c //t依附于主线程 void TestThread::run() —— end */
(4)研究执行槽函数线程的意义
当信号的发送与对应槽函数的执行是在不同的线程中时,可能产生临界资源的竞争问题。如同一个线程类中的run()与槽函数可能就会被不同的线程执行,这时可能出现上述问题。
2. 线程中的事件循环
(1)采用队列连接时,信号与槽的机制需要事件循环的支持。
(2)QThread类中提供的exec()函数用于开启线程的事件循环
(3)发往事件队列的信号会在事件循环中被特定的槽函数接收。只有开启了事件循环,对象中的槽函数才会在依附中线程中被调用。
【编程实验】线程的事件循环
//MyObject.h(同上例)
#ifndef MYOBJECT_H #define MYOBJECT_H #include <QObject> class MyObject : public QObject { Q_OBJECT public: explicit MyObject(QObject *parent = 0); signals: protected slots: void testSlot(); }; #endif // MYOBJECT_H
//MyObject.cpp(同上例)
#include "MyObject.h" #include <QThread> #include <QDebug> MyObject::MyObject(QObject *parent) : QObject(parent) { } void MyObject::testSlot() { qDebug() << "void MyObject::testSlot() tid = " << QThread::currentThreadId(); }
//TestThread.h
#ifndef TESTTHREAD_H #define TESTTHREAD_H #include <QThread> class TestThread : public QThread { Q_OBJECT protected: void run(); public: explicit TestThread(QObject *parent = 0); signals: void testSignal(); void innerSignal(); }; #endif // TESTTHREAD_H
//TestThread.cpp
#include "TestThread.h" #include <QDebug> TestThread::TestThread(QObject *parent) : QThread(parent) { } void TestThread::run() { qDebug() << "void TestThread::run() -- begin tid = " << currentThreadId(); emit innerSignal(); exec(); //当信号和槽使用队列连接时,必须调用exec来开启事件循环。直接连接时则不必开启 qDebug() << "void TestThread::run() -- end"; }
//main.cpp
#include <QCoreApplication> #include <QThread> #include <QDebug> #include <QMutex> #include "TestThread.h" #include "MyObject.h" /*演示多线程的信号和槽*/ //结论:当使用队列连接时,需要开启事件循环。直接连接时则不需要。 //自定义消息处理程序,让QDebug输出是线程安全的。 void messageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) { QByteArray localMsg = msg.toLocal8Bit(); //fcntl(stderr, ); static QMutex mutex; mutex.lock(); switch (type) { case QtDebugMsg: fprintf(stderr, "%s\n", localMsg.constData()); break; case QtInfoMsg: fprintf(stderr, "Info: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); break; case QtWarningMsg: fprintf(stderr, "Warning: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); break; case QtCriticalMsg: fprintf(stderr, "Critical: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); break; case QtFatalMsg: fprintf(stderr, "Fatal: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); abort(); } mutex.unlock(); } int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); // 安装消息处理程序 qInstallMessageHandler(messageOutput); qDebug() << "main() tid = " << QThread::currentThreadId(); TestThread t; MyObject m; //测试1:收发都在主线程(默认连接,子线程无需开启事件循环) //QObject::connect(&t, SIGNAL(testSignal()), &m, SLOT(testSlot())); //emit t.testSignal(); //测试2:主线程发射,子线程接收(默认连接:队列连接,需要开启事件循环) //QObject::connect(&t, SIGNAL(testSignal()), &m, SLOT(testSlot())); //m.moveToThread(&t); //emit t.testSignal(); //测试3:主线程发射,子线程接收(改为直接连接,无需开启事件循环) //QObject::connect(&t, SIGNAL(testSignal()), &m, SLOT(testSlot()),Qt::DirectConnection); //m.moveToThread(&t); //emit t.testSignal(); //测试4:子线程发射,子线程接收(默认连接:直接连接,无需开启事件循环) //QObject::connect(&t, SIGNAL(innerSignal()), &m, SLOT(testSlot())); //m.moveToThread(&t); //测试5:子线程发射,子线程接收(改为队列连接,无需开启事件循环) QObject::connect(&t, SIGNAL(innerSignal()), &m, SLOT(testSlot()), Qt::QueuedConnection); m.moveToThread(&t); t.start(); return a.exec(); }
3. 小结
(1)默认情况下:对象依附于自身被创建的线程,而对象中的槽函数在依附的线程中被调用执行。
(2)QObject::moveToThread用于改变对象的线程依附性
(3)采用队列连接时,信号和槽机制需要事件循环的支持
(4)exec()函数用于开启线程的事件循环
原文:http://www.cnblogs.com/5iedu/p/6486518.html