Qt——多线程中的信号与槽(二) 📅 2026/7/15 5:13:50 1.上节实验发现当槽函数是线程类中的成员时为什么依然不在本线程内被调用执行2.隐藏的问题对象依附于哪一个线程默认情况下对象依附于自身被创建的线程例如对象在主线程main()函数中被创建则依附于主线程对象的依附性与槽函数执行的关系默认情况下槽函数在其所依附的线程中被调用执行对象的依附性是否可以改变QObject::moveToThread用于改变对象的线程依附性使得对象的槽函数在依附的线程中被调用执行MyObject.h#ifndef MYOBJECT_H #define MYOBJECT_H #include QObject class MyObject : public QObject { Q_OBJECT public: explicit MyObject(QObject *parent nullptr); signals: protected slots: void getStarted(); void testSlot(); }; #endif // MYOBJECT_HMyObject.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() 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 nullptr); signals: void testSignal(); protected slots: void testSlot(); }; #endif // TESTTHREAD_HTestThread.cpp#include TestThread.h #include QDebug TestThread::TestThread(QObject *parent) : QThread{parent} { connect(this, SIGNAL(testSignal()), this, SLOT(testSlot())); } void TestThread::run() { qDebug() void TestThread::run() — — begin tid QThread::currentThreadId(); for(int i0; i5; i) { qDebug() void TestThread::run() i i; sleep(1); } emit testSignal(); qDebug() void TestThread::run() — — end; } void TestThread::testSlot() { qDebug() void TestThread::testSlot() tid QThread::currentThreadId(); }main.cpp#include QCoreApplication #include QDebug #include QThread #include TestThread.h #include MyObject.h int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); qDebug() main() tid QThread::currentThreadId(); TestThread t; MyObject m; QObject::connect(t, SIGNAL(started()), m, SLOT(getStarted())); QObject::connect(t, SIGNAL(testSignal()), m, SLOT(testSlot())); //m.moveToThread(t); //改变m线程的依附性依附于t t.start(); return QCoreApplication::exec(); }运行结果main() tid 0x30b8void TestThread::run() — — begin tid 0x1b78void TestThread::run() i 0void MyObject::getStarted() tid 0x30b8void TestThread::run() i 1void TestThread::run() i 2void TestThread::run() i 3void TestThread::run() i 4void TestThread::run() — — endvoid TestThread::testSlot() tid 0x30b8void MyObject::testSlot() tid 0x30b8将main.cpp中的m.moveToThread(t);取消注释后的运行结果主线程创建TestThread t和MyObject m连接信号槽t的started()→m的getStarted()跨线程t的testSignal()→m的testSlot()跨线程t的testSignal()→t的testSlot()同一对象同一线程m.moveToThread(t)→m移到子线程t.start()→ 启动子线程main() tid 0x3b40void TestThread::run() — — begin tid 0x16e4void TestThread::run() i 0void TestThread::run() i 1void TestThread::run() i 2void TestThread::run() i 3void TestThread::run() i 4void TestThread::run() — — endvoid TestThread::testSlot() tid 0x3b40我们发现改变m的依附性后m的槽函数没有被执行3.线程中的事件循环信号与槽的机制需要事件循环的支持QThread类中提供的exec()函数用于开启线程的事件循环只有事件循环开启槽函数才能在信号发送后被调用所以前提条件对象依附的线程开启了事件循环后置结果对象中的槽函数在依附的线程中被调用执行我们将TestThread.cpp中的run()函数中加入exec();语句后并且将t的依附性改为t的结果是main() tid 0x330void MyObject::getStarted() tid 0x6ecvoid TestThread::run() — — begin tid 0x6ecvoid TestThread::run() i 0void TestThread::run() i 1void TestThread::run() i 2void TestThread::run() i 3void TestThread::run() i 4void TestThread::testSlot() tid 0x6ecvoid MyObject::testSlot() tid 0x6ec研究这个的意义在于当信号的发送与对应槽函数的执行在不同线程中可能产生临界资源的竞争问题4.但是上述代码加入exec()后开启事件循环我们发现无法exec()后面的代码无法执行那么线程如何正常结束事件循环结束前exec()后的语句无法执行quit()和exit()函数用于结束事件循环quit()——exit(0)exec()的返回值由exit()参数决定无论事件循环是否开启信号发送后会直接进入对象所依附的事件队列然而只有开启了事件循环对应的槽函数才会在线程中被调用所以解决方案是在main.cpp中加入t.wait(8 * 1000);t.quit();即等待8秒如果线程还没有结束就向下执行5.什么时候需要在线程中开启事件循环事务性操作间断性IO操作等可以开启线程的事件循环每次操作通过发送信号的方式使得槽函数在子线程中执行示例写入文件FileWriter.cpp#ifndef FILEWRITER_H #define FILEWRITER_H #include QFile #include QObject #include QThread class FileWriter : public QObject { Q_OBJECT class Worker : public QThread { protected: void run(); }; QFile m_file; Worker m_worker; public: explicit FileWriter(QString file, QObject* parent 0); bool open(); void write(QString text); void close(); ~FileWriter(); signals: void doWrite(QString text); void doClose(); protected slots: void writeSlot(QString text); void closeSlot(); }; #endif // FILEWRITER_HFileWriter.h#include FileWriter.h #include QDebug #include QString void FileWriter::Worker::run() { qDebug() void FileWriter::Worker::run() - begin: tid currentThreadId(); exec(); qDebug() void FileWriter::Worker::run() - end; } FileWriter::FileWriter(QString file, QObject* parent) : QObject(parent), m_file(file) { connect(this, SIGNAL(doWrite(QString)), this, SLOT(writeSlot(QString))); connect(this, SIGNAL(doClose()), this, SLOT(closeSlot())); moveToThread(m_worker); m_worker.start(); } bool FileWriter::open() { return m_file.open(QIODevice::WriteOnly | QIODevice::Text); } void FileWriter::write(QString text) { qDebug() void FileWriter::write(QString text) tid QThread::currentThreadId(); emit doWrite(text); } void FileWriter::close() { qDebug() void FileWriter::close() tid QThread::currentThreadId(); emit doClose(); } void FileWriter::writeSlot(QString text) { qDebug() void FileWriter::writeSlot(QString text) tid QThread::currentThreadId(); m_file.write(text.toLatin1()); m_file.flush(); //强制将缓冲区中的数据立即写入物理存储设备所以write函数是一个很低效的事务 } void FileWriter::closeSlot() { qDebug() void FileWriter::closeSlot() tid QThread::currentThreadId(); m_file.close(); } FileWriter::~FileWriter() { m_worker.quit(); }main.cpp#include QCoreApplication #include QDebug #include QThread #include FileWriter.h int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); qDebug() main() tid QThread::currentThreadId(); FileWriter writer(C:\\Users\\聪\\Desktop\\1.txt); if( writer.open() ) { writer.write(hello); writer.write(11); writer.write(22); writer.close(); } return QCoreApplication::exec(); }运行结果main() tid 0x9d0void FileWriter::Worker::run() - begin: tid 0x2108void FileWriter::write(QString text) tid 0x9d0void FileWriter::write(QString text) tid 0x9d0void FileWriter::write(QString text) tid 0x9d0void FileWriter::close() tid 0x9d0void FileWriter::writeSlot(QString text) tid 0x2108void FileWriter::writeSlot(QString text) tid 0x2108void FileWriter::writeSlot(QString text) tid 0x2108void FileWriter::closeSlot() tid 0x2108