你的浏览器不支持canvas

正确且优雅的使用QThread

Date: Author: zhangn

本文章采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可。转载请注明来自zhangn的云笔记

我们在写Qt多线程的时候,都会直接重写QThread类的run方法,其实这种方式是Qt的设计者所反对的,它违背了QThread类的设计初衷,QThread的设计者曾发帖说过这个问题,原文找不到了,我们今天来看一下如何在不重写run方法的情况下使用QThread

首先是控制类实现

//Controller.h
#ifndef CONTROLLER_H
#define CONTROLLER_H

#include <QObject>

class QThread;
class Controller : public QObject
{
	Q_OBJECT

public:
	Controller(QObject *parent = nullptr);
	~Controller();

	void start();

signals:
	void operate();

private:
	QThread *m_pThread;
};

#endif // CONTROLLER_H

//Controller.cpp
#include "Controller.h"
#include "Worker.h"
#include <QThread>

Controller::Controller(QObject *parent)
	: QObject(parent)
{
	m_pThread = new QThread;

	// 要调用moveToThread的QObject子类实例,不能有父类指针
	// 因此只能绑定QObject::deleteLater释放内存
	Worker *pWorker = new Worker;
	pWorker->moveToThread(m_pThread);

	connect(m_pThread, &QThread::finished, pWorker, &QObject::deleteLater);
	connect(this, &Controller::operate, pWorker, &Worker::doWork);
}

Controller::~Controller()
{
	if (m_pThread)
	{
		m_pThread->requestInterruption();
		m_pThread->quit();
		m_pThread->wait();

		delete m_pThread;
		m_pThread = nullptr;
	}
}

void Controller::start()
{
	// 启动线程
	m_pThread->start();

	// 必须用信号曹的方式执行工作
	// 如果直接调用则不会在线程中工作
//	m_pWorker->doWork();
//	emit operate();
}

然后是工作类实现

//Worker.h
#ifndef WORKER_H
#define WORKER_H

#include <QObject>

class Worker : public QObject
{
	Q_OBJECT

public:
	Worker(QObject *parent = nullptr);
	~Worker();

	void doWork();

private:
	
};

#endif // WORKER_H

//Worker.cpp
#include "Worker.h"
#include <stdio.h>
#include <QThread>

Worker::Worker(QObject *parent)
	: QObject(parent)
{

}

Worker::~Worker()
{

}

void Worker::doWork()
{
	while (!QThread::currentThread()->isInterruptionRequested())
	{
		QString threadText = QStringLiteral("@0x%1").arg(quintptr(QThread::currentThreadId()), 16, 16, QLatin1Char('0'));
	//	QString threadText = QString::number(quintptr(QThread::currentThreadId()));
		printf("worker = %d\n", threadText.toUInt());
		QThread::sleep(1);
	}
}

最后是main函数

#include <QtCore/QCoreApplication>
#include "Controller.h"
#include <QThread>

int main(int argc, char *argv[])
{
	QCoreApplication a(argc, argv);

	// 工作信号在哪发射的无所谓,但必须有人发射;
	// 信号和线程开始谁先调用也无所谓,中间有时间间隔也没影响,
	// 只有两个都被调用了才会在线程中工作;
	// 如果先调用线程开始,则线程会启动,但工作代码不执行;
	// 如果先发射信号,线程不启动,工作代码也不执行,
	// 只有在线程启动后工作代码才会在线程中执行;
	// 如果不使用信号而是直接调用工作代码,线程启动与否已经不重要,
	// 即使线程已经启动,也不会在线程中执行,而是在当前线程中运行
	Controller *controller = new Controller;

//	emit controller->operate();
//	QThread::sleep(5);
	controller->start();
//	emit controller->operate();

	QThread::sleep(5);

	emit controller->operate();
	QThread::sleep(5);
	delete controller;

// 	while (true)
// 	{
// 		QString threadText = QString::number(quintptr(QThread::currentThreadId()));
// 		QByteArray ba = threadText.toLocal8Bit();
// 		printf("main = %d\n", ba.data());
// 		QThread::sleep(1);
// 	}

	return a.exec();
}

对于本文内容有问题或建议的小伙伴,欢迎在文章底部留言交流讨论。