[Qt][Qt 多线程][上]详细讲解

DieSnowK 2024-09-08 15:35:01 阅读 67

目录

0.Qt 多线程概述1.如何看待客户端的多线程?2.Qt 线程的使用条件3.创建线程的方法1.方法一2.方法二3.说明4.关闭线程使用的方法


0.Qt 多线程概述

Qt中,多线程的处理⼀般是通过<code>QThread类来实现QThread代表⼀个在应⽤程序中可以独⽴控制的线程,也可以和进程中的其他线程共享数据QThread对象管理程序中的⼀个控制线程,QThreadrun()中开始执⾏

默认情况下,run()通过调⽤exec()来启动事件循环,并在线程内运⾏Qt事件循环


1.如何看待客户端的多线程?

服务器利用多线程,最主要的目的是充分利用多核CPU的计算资源客户端对于普通用户,"使用体验"很重要

客户端上的程序很少会使用多线程把CPU计算资源吃完主要是利用多线程执行一些耗时的等待IO的操作,避免主线程被卡死,避免对用户造成不好的体验


2.Qt 线程的使用条件

在Qt中,多线程常⽤于⽐较耗时的任务,或只有通过使⽤线程执⾏时才能正常运⾏的情况


3.创建线程的方法

1.方法一

继承QThread类,重写run()函数 --> 多态步骤

⾃定义⼀个类,继承于QThread,并且只有⼀个线程处理函数重写⽗类中的run()线程处理函数⾥⾯写⼊需要执⾏的复杂数据处理启动线程不能直接调⽤run(),需要使⽤对象来调⽤start()实现线程启动

start()底层就是调用OS API创建线程,新县城创建后自动执行run() 线程处理函数执⾏结束后可以定义⼀个信号来告诉主线程最后关闭线程 示例:定时器

// thread.h

class Thread : public QThread

{

Q_OBJECT

public:

Thread();

// 重要的目的是重写父类的 run 方法.

void run();

signals:

void Notify();

};

// thread.cpp

void Thread::run()

{

// 每过一秒, 通过信号槽, 来通知主线程, 负责更新的界面内容

for (int i = 0; i < 10; i++)

{

// sleep 本身是 QThread 的成员函数, 就可以直接调用

sleep(1);

// 发送一个信号, 通知主线程

emit Notify();

}

}

-------------------------------------------------------------------------

// widget.h

class Widget : public QWidget

{

// ...

Thread thread;

void Handle();

};

// widget.cpp

// 构造函数中

{

// 连接信号槽, 通过槽函数更新界面

connect(&thread, &Thread::Notify, this, &Widget::Handle);

// 要启动一下线程.

thread.start();

}

void Widget::handle()

{

// 此处修改界面内容.

int value = ui->lcdNumber->intValue();

value--;

ui->lcdNumber->display(value);

}


2.方法二

继承QObject类,通过moveToThread(thread),交给thread执⾏函数原型moveToThread(QThread* targetThread)功能:将⼀个对象移动到指定的线程中运⾏步骤

⾃定义⼀个类,继承于QObject类创建⼀个⾃定义线程类的对象,不能指定⽗对象创建⼀个QThread类的对象,可以指定其⽗对象将⾃定义线程对象加⼊到QThread类的对象中使⽤使⽤start()启动线程

调⽤start()只是启动了线程,但是并没有开启线程处理函数线程处理函数的开启需要⽤到信号槽机制 关闭线程 示例

// thread.h

class MyThread : public QObject

{

Q_OBJECT

public:

// ...

void Thread();

void SetFlag(bool flag = true);

signals:

void Notify();

private:

bool isStop() = false;

};

// thread.cpp

void MyThread::Thread()

{

while(!isStop)

{

QThread::sleep(1);

emit Notofy();

qDebug() << " ⼦线程号: " << QThread::currentThread();

if(isStop)

{

break;

}

}

}

void MyThread::SetFlag(bool flag)

{

isStop = flag;

}

-------------------------------------------------------------------------

// widget.h

class Widget : public QWidget

{

// ...

MyThread* myThread;

QThread* thread;

signals:

void StartSignal(); // 启动子线程信号

private slot:

void on_startPushbutton_clicked();

void on_closePushbutton_clicked();

void DelSignals();

void DealClose();

};

// widget.cpp

// 构造函数中

{

// 动态分配空间,不能指定⽗对象

myThread = new MyThread();

// 创建子线程

thread = new QThread(this);

// 将⾃定义的线程加⼊到⼦线程中

myThread->moveToThread(thread);

connect(myThread, &MyThread::Notify, this, &Widget::DelSignals);

connect(this, &Widget::StartSignal, myThread, &MyThread::Thread);

connect(this, &Widget::destroyed, this, &Widget::DealClose);

}

void MyWidget::on_startPushbutton_clicked()

{

if(thread->isRunning() == true)

{

return;

}

// 启动线程但是没有启动线程处理函数

thread->start();

// 不能直接调⽤线程处理函数,直接调⽤会导致线程处理函数和主线程处于同⼀线程

emit StartSignal();

}

void MyWidget::DelSignals()

{

static int i = 0;

i++;

ui->lcdNumber->display(i);

}

void MyWidget::on_closePushbutton_clicked()

{

if(thread->isRunning() == false)

{

return;

}

myThread->SetFlag();

thread->quit();

thread->wait();

}

void MyWidget::DealClose()

{

delete myThread;

on_closePushbutton_clicked();

}


3.说明

线程函数内部不允许操作UI图形界⾯,⼀般⽤数据处理

只有主线程可以操作UI图形界面 connect()第五个参数表⽰的是连接的⽅式,且只有在多线程的时候才意义connect()第五个参数Qt::ConnectionType,⽤于指定信号和槽的连接类型,同时影响信号的传递⽅式和槽函数的执⾏顺序

Qt::AutoConnection:会根据信号和槽函数所在的线程⾃动选择连接类型

如果信号和槽函数在同⼀线程中,那么使⽤Qt:DirectConnection类型如果它们位于不同的线程中,那么使⽤Qt::QueuedConnection类型 Qt::DirectConnection:当信号发出时,槽函数会⽴即在同⼀线程中执⾏

适⽤于信号和槽函数在同⼀线程中的情况可以实现直接的函数调⽤,但需要注意线程安全性 Qt::QueuedConnection:当信号发出时,槽函数会被插⼊到接收对象所属的线程的事件队列中,等待下⼀次事件循环时执⾏

适⽤于信号和槽函数在不同线程中的情况,可以确保线程安全 Qt::BlockingQueuedConnection:与Qt:QueuedConnection类似

但是发送信号的线程会被阻塞,直到槽函数执⾏完毕适⽤于需要等待槽函数执⾏完毕再继续的场景,但需要注意可能引起线程死锁的⻛险 Qt::UniqueConnection:这是⼀个标志,可以使⽤|与上述任何⼀种连接类型组合使⽤


4.关闭线程使用的方法

void terminate():直接关闭线程不等待线程任务结束void quit():等待线程任务结束之后关闭线程



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。