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_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() << "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_H
TestThread.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 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 = " << 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 = 0x30b8
void TestThread::run() --- --- begin tid = 0x1b78
void TestThread::run() i 0
void MyObject::getStarted() tid = 0x30b8
void TestThread::run() i 1
void TestThread::run() i 2
void TestThread::run() i 3
void TestThread::run() i 4
void TestThread::run() --- --- end
void TestThread::testSlot() tid = 0x30b8
void 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 = 0x3b40
void TestThread::run() --- --- begin tid = 0x16e4
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::run() --- --- end
void TestThread::testSlot() tid = 0x3b40
我们发现,改变m的依附性后,m的槽函数没有被执行
3.线程中的事件循环
- 信号与槽的机制需要事件循环的支持
- QThread类中提供的exec()函数用于开启线程的事件循环
- 只有事件循环开启,槽函数才能在信号发送后被调用
所以:前提条件:对象依附的线程开启了事件循环,后置结果:对象中的槽函数在依附的线程中被调用执行
我们将TestThread.cpp中的run()函数中加入exec();语句后,并且将t的依附性改为t的结果是:
main() tid = 0x330
void MyObject::getStarted() tid = 0x6ec
void TestThread::run() --- --- begin tid = 0x6ec
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 = 0x6ec
void 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_H
FileWriter.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 = 0x9d0
void FileWriter::Worker::run() - begin: tid = 0x2108
void FileWriter::write(QString text) tid = 0x9d0
void FileWriter::write(QString text) tid = 0x9d0
void FileWriter::write(QString text) tid = 0x9d0
void FileWriter::close() tid = 0x9d0
void FileWriter::writeSlot(QString text) tid = 0x2108
void FileWriter::writeSlot(QString text) tid = 0x2108
void FileWriter::writeSlot(QString text) tid = 0x2108
void FileWriter::closeSlot() tid = 0x2108