QT多线程基础

简介

相关名词

  • 同步Sync VS 异步Async
    • 同步(手动查询):我调用一个功能,该功能没有结束前,我不断手动查询结果。
    • 异步(主动通知):我调用一个功能,不需要知道该功能结果,该功能有结果后通知我。
  • 阻塞Block VS 非阻塞Unblock
    • 阻塞: 调用我(函数),我(函数)没有接收完数据或者没有得到结果之前,我不会返回。
    • 非阻塞:调用我(函数),我(函数)立即返回,通知调用者。
  • 回调
    • 同步回调:阻塞(无需等待回调完成即可完成其执行)。
    • 异步回调:非阻塞(无需等待回调完成即可完成其执行)。
  • 进程 VS 线程 VS 协程
  • 并发concurrency VS 并行parallelism
    • 并发(交替进行):并发包含多线程,多线程是并发的一种实现方式。
    • 并行(同时进行):多核处理器。
  • 事件循环

QT

使用QThread类。
作用:用线程来处理那些耗时的后台操作,从而让主界面能及时响应用户的请求操作。

运行方式

QThread 的执行从 run() 函数的执行开始

  • run() 函数通过调用 exec() 函数来启动事件循环机制,并且在线程内部处理 Qt 的事件。

基础使用方法

  • QObject::moveToThread()
  • 继承 QThread 类:子类化QThread,然后重写run函数: 旧方法,不推荐
    • run() 中未调用 exec() 开启 even loop =》 run() 执行结束时,线程自动退出。
    • 如果在 WorkerThread 的 run() 中使用了 WorkerThread 的成员变量,而且 QThread的其他方法也使用到了它,即我们从不同线程访问该成员变量,这时需要自行检查安全性

void QObject::moveToThread ( QThread * targetThread )

不能认为 monitor 的控制权归属于新线程(在哪里创建就属于哪里)!仅有槽函数在指定线程中调用,包括构造函数都仍然在主线程中调用。

  • 若使用默认的 run() 方法或自行调用 exec() ,则QThread将开启事件循环
    • QThread->start() 开启线程(调用了 QThread 的 run() 默认开启事件循环)。
    void QThread::run()
    {
        (void) exec();
    }
    
  • QThread 提供 exit() 函数和 quit() 槽
    • 赋予了QThread使用需要事件循环的非GUI类的能力(QTimer、QTcpSocket 等)。
    • 也使得该线程可以关联任意一个线程的信号到指定线程的槽函数。如果一个线程没有开启事件循环,那么该线程中的 timeout() 将永远不会发射。

退出线程过程

在删除 QThread 之前,需要等待 finish 信号 (删除正在运行的 QThread 将导致程序奔溃)。
删除 QThread 对象并不会停止其管理的线程的执行。

  1. 对于未开启事件循环的线程
    仅需让 run() 执行结束即可终止线程:通过 bool 变量进行控制,并定义一个 QMutex 进行加锁保护

  2. 对于开启了事件循环的线程:正常的退出线程其实质是退出事件循环
    若线程中开始开启了 EvenLoop,耗时代码执行结束后,线程并不会退出。退出方法:

  • quit()/exit() + wait()
  • terminate()+ wait():不推荐(强制结束线程是危险的操作)
  • finished信号
connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater);

wait(): 等待子线程的结束

Qt - 一文理解QThread多线程(万字剖析整理)
You’re doing it wrong…

实例

  1. Worker类(耗时程序逻辑处理)
// worker.h
class Worker : public QObject
{
    Q_OBJECT
public:
    explicit Worker(QObject *parent = nullptr);
    ~Worker();

public slots:
    void doSomething(const QString& cmd);

signals:
    void resultNotify(const QString& des);
};

// worker.cpp
Worker::Worker(QObject *parent)
    : QObject(parent)
{
}

Worker::~Worker()
{
}

void Worker::doSomething(const QString &cmd)  // 耗时程序
{
    qDebug() << "doSomething()" << cmd << "thread:" << QThread::currentThreadId();
    emit resultNotify("doSomething ok!");
}
  1. Controller类: 外部调用通过Controlleroperate发射信号给workerworker进行耗时程序处理。worker处理完成后,发射信号,controllerhandleResults槽函数接收,确认程序是否正常。
// Controller.h
class Controller : public QObject
{
    Q_OBJECT
public:
    Controller(QObject* parent = nullptr);
    ~Controller();

public slots:
    void handleResults(const QString &des);

signals:
    void operate(const QString &cmd);

private:
    QThread thread;
};

// Controller.cpp
Controller::Controller(QObject* parent)
    : QObject(parent)
{
    Worker *worker = new Worker();
    worker->moveToThread(&thread);  // 移入线程
	connect(this, &Controller::operate, worker, &Worker::doSomething);
    connect(&thread, &QThread::finished, worker, &QObject::deleteLater);  
    connect(worker, &Worker::resultNotify, this, &Controller::handleResults);
    thread.start();
}

Controller::~Controller()
{
    thread.quit();
    thread.wait();
}

void Controller::handleResults(const QString &des)
{
    qDebug() << "handleResults()" << des << "thread:" << QThread::currentThreadId();
}
  1. 调用:
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    Controller* controller = new Controller(this);
    emit controller->operate("copy");
}

https://blog.csdn.net/zyhse/article/details/106313994

QT锁QMutex

QMutexLocker

不能够定义 私有成员变量 和 全局变量,只能够定义局部变量来使用。

使用方法:
(1)先定义一个QMutex类的变量
QMutex m_mutex; (可以是私有成员变量,也可以是全局变量)
(2) 在定义一个QMutexLocker类的变量(注意:在需要上锁的地方直接定义即可)

QMutexLocker locker(&mutex);
a = 5; //等等需要进行写的操作

使用注意:
(1)如果需要对一个全局变量区域进行保护,那么QMutex定义的变量就得是全局的!
(2)QMutexLocker上锁,解锁的原理:在该局部变量被创建的时候上锁,当所在函数运行完毕后该QMutexLocker局部变量在栈中销毁掉(解锁)。
注意,如果该局部变量在中间被打断,那么QMutexLocker上的锁就不会被解锁掉,因为该函数没有被完整的是执行完。QMutexLocker所创建的局部变量也没有被正确销毁销毁,可能就和QMutexLocker他自己本身的机制不服也就不会解锁。

https://www.cnblogs.com/xiangtingshen/p/11078381.html

高级接口

标准库中的future、promise,Qt中的QFuture、QFutureWatcher等。

单例模式(线程安全)

饿汉式单例

class Singleton
{
private:
	Singleton();  // private类型的构造函数,外部(其他类对象)不能直接new一个该对象的实例
public:
	static bool GetSingleton();   // 该类唯一的一个public方法
private:
   static Singleton* m_pSingleton = new Singleton();  // 直接初始化一个实例对象
}

懒汉式单例

class Singleton
{
private:
	Singleton();
public:
	static Singleton GetInstance();
private:
   static Singleton* m_pInstance;
}

Singleton GetInstance()
{
	if (instance == NULL)
	{
		m_pInstance = new Singleton();
	}
	return m_pInstance
}

在多线程并发下这样的实现是无法保证实例实例唯一的,多个线程可以同时进入getInstance()方法。

解决方案

  • 加同步锁 =》 效率低
  • 同步代码块 =》 效率低
  • 使用静态内置类
class Singleton
{
private:
	Singleton();
	static class MySingletonHandler{
		private static MySingleton instance = new MySingleton();
	}
public:
	static Singleton GetInstance();
}

Singleton GetInstance()
{
	return MySingletonHandler.instance;
}
  • 双检查锁机制
Singleton getInstance(){    //对获取实例的方法进行同步
	if (instance == null){
		synchronized(Singleton.class){
			if (instance == null)
				instance = new Singleton();
       }
   }
   return instance;
}

https://blog.csdn.net/cselmu9/article/details/51366946

QT获取线程ID(仅调试使用)

QT获取线程号函数currentThreadId()返回Qt::HANDLE 如何得到QString?

(int)currentThreadId()

问题、Warning

  1. 在加锁状态下无法停止线程,需先解锁。
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Qt是一个跨平台的应用程序框架,提供了丰富的多线程编程支持。Qt多线程编程主要依靠QThread类和信号槽机制来实现。 QThread类封装了线程的基本操作,使得我们可以通过继承这个类来实现自己的线程。通过重写QThread类中的run()方法,我们可以在这个方法中实现具体的线程操作。 例如,下面是一个简单的QThread子类的定义: ```c++ class MyThread : public QThread { Q_OBJECT public: explicit MyThread(QObject *parent = nullptr); protected: void run() override; signals: void resultReady(int result); }; ``` 在这个例子中,我们重写了run()方法来实现线程的具体操作。在这个方法中,我们可以调用其他Qt类或者自己实现的函数来完成多线程的任务。另外,我们还定义了一个resultReady信号,用于在线程执行完毕后向主线程发送消息。 接下来,我们可以在主线程中创建一个MyThread对象,并连接它的resultReady信号到一个槽函数中,以便在线程执行完毕后处理结果。例如: ```c++ MyThread *thread = new MyThread(this); connect(thread, &MyThread::resultReady, this, &MyClass::handleResult); thread->start(); ``` 在这个例子中,我们创建一个MyThread对象并启动它。在线程执行完毕后,它会发送resultReady信号,我们将这个信号连接到handleResult槽函数中来处理结果。 除了QThread类外,Qt还提供了许多其他的多线程编程工具,如QThreadPool类、QMutex类、QWaitCondition类等,可以帮助我们更方便地实现多线程编程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值