一.多线程的原理和功能
1.多线程(multithreading)是指从软件或者硬件上实现多个线程并发执行的技术。
2.多线程的功能和作用主要包括:
提高程序的并发性和效率:多线程可以同时执行多个任务,不同的线程可以同时读写不同的数据,从而避免了线程之间的阻塞等待,提高了程序的效率和响应速度。
解决负载均衡问题,充分利用CPU的资源:通过多线程,可以同时完成多件事情而互不干扰,提高cpu的使用效率。
异步处理:将程序中占据时间长的任务放到后台去处理,如图片、视频的下载,从而提高用户体验。
发挥多核处理器的优势:通过并发执行,让系统运行的更快、更流畅。
3.多线程程序缺点:
线程之间的共享资源会引发竞争条件,造成数据不一致问题,需要使用同步机制来解决。同时,多线程程序也会增加程序的复杂度和调试难度,需要谨慎设计和维护。
二.QT中的多线程
1.QThread线程基础
QThread是Qt线程中有一个公共的抽象类,所有的线程类都是从QThread抽象类中派生的,需要实现QThread中的虚函数run(),通过start()函数来调用run函数。
void run()函数是线程体函数,用于定义线程的功能。
void start()函数是启动函数,用于将线程入口地址设置为run函数。
void terminate()函数用于强制结束线程,不保证数据完整性和资源释放。
QCoreApplication::exec()总是在主线程(执行main()的线程)中被调用,不能从一个QThread中调用。在GUI程序中,主线程也称为GUI线程,是唯一允许执行GUI相关操作的线程。另外,必须在创建一个QThread前创建QApplication(or QCoreApplication)对象。
当线程启动和结束时,QThread会发送信号started()和finished(),可以使用isFinished()和isRunning()来查询线程的状态。
从Qt4.8起,可以释放运行刚刚结束的线程对象,通过连接finished()信号到QObject::deleteLater()槽。
使用wait()来阻塞调用的线程,直到其它线程执行完毕(或者直到指定的时间过去)。
静态函数currentThreadId()和currentThread()返回标识当前正在执行的线程。前者返回线程的ID,后者返回一个线程指针。
要设置线程的名称,可以在启动线程之前调用setObjectName()。如果不调用setObjectName(),线程的名称将是线程对象的运行时类型(QThread子类的类名)。
2.线程的优先级
QThread线程总共有8个优先级
QThread::IdlePriority 0 scheduled only when no other threads are running.
QThread::LowestPriority 1 scheduled less often than LowPriority.
QThread::LowPriority 2 scheduled less often than NormalPriority.
QThread::NormalPriority 3 the default priority of the operating system.
QThread::HighPriority 4 scheduled more often than NormalPriority.
QThread::HighestPriority 5 scheduled more often than HighPriority.
QThread::TimeCriticalPriority 6 scheduled as often as possible.
QThread::InheritPriority 7 use the same priority as the creating thread. This is the default.
3.void setPriority(Priority priority)
设置正在运行线程的优先级。如果线程没有运行,此函数不执行任何操作并立即返回。使用的start()来启动一个线程具有特定的优先级。优先级参数可以是QThread::Priority枚举除InheritPriortyd的任何值。
4.线程的创建
void start ( Priority priority = InheritPriority )
启动线程执行,启动后会发出started ()信号
5.线程的执行
int exec() [protected]
进入事件循环并等待直到调用exit(),返回值是通过调用exit()来获得,如果调用成功则返回0。
void run() [virtual protected]
线程的起点,在调用start()之后,新创建的线程就会调用run函数,默认实现调用exec(),大多数需要重新实现run函数,便于管理自己的线程。run函数返回时,线程的执行将结束。
6.线程的退出
void quit();
通知线程事件循环退出,返回0表示成功,相当于调用了QThread::exit(0)。
void exit ( int returnCode = 0 );
调用exit后,thread将退出event loop,并从exec返回,exec的返回值就是returnCode。通常returnCode=0表示成功,其他值表示失败。
void terminate ();
结束线程,线程是否立即终止取决于操作系统。
线程被终止时,所有等待该线程Finished的线程都将被唤醒。
terminate是否调用取决于setTerminationEnabled ( bool enabled = true )开关。
void requestInterruption()
请求线程的中断。请求是咨询意见并且取决于线程上运行的代码,来决定是否及如何执行这样的请求。此函数不停止线程上运行的任何事件循环,并且在任何情况下都不会终止它。
7.线程的等待
bool wait ( unsigned long time = ULONG_MAX )
线程将会被阻塞,等待time毫秒,如果线程退出,则wait会返回。Wait函数解决多线程在执行时序上的依赖。
void msleep ( unsigned long msecs )
void sleep ( unsigned long secs )
void usleep ( unsigned long usecs )
sleep()、msleep()、usleep()允许秒,毫秒和微秒来区分,但在Qt5.0中被设为public。
一般情况下,wait()和sleep()函数应该不需要,因为Qt是一个事件驱动型框架。考虑监听finished()信号来取代wait(),使用QTimer来取代sleep()。
8.线程的状态
bool isFinished () const 线程是否已经退出
bool isRunning () const 线程是否处于运行状态
9.线程的属性
Priority priority () const
void setPriority ( Priority priority )
uint stackSize () const
void setStackSize ( uint stackSize )
void setTerminationEnabled ( bool enabled = true ) 设置是否响应terminate()函数
三.代码示例
- mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
//class QThread;
class QString;
class MyThread : public QThread
{
public:
MyThread();
void setMessage(const QString &message);
void stop();
protected:
void run();
private:
QString m_MessageStr;
volatile bool m_Stopped;
};
#endif // MYTHREAD_H
2.mythread.cpp
#include "mythread.h"
#include <QDebug>
MyThread::MyThread()
{
m_Stopped = false;
}
void MyThread::setMessage(const QString &message)
{
m_MessageStr = message;
}
void MyThread::stop()
{
m_Stopped = true;
}
void MyThread::run()
{
while(!m_Stopped)
{
qDebug()<<m_MessageStr;
msleep(500);
}
m_Stopped = false;
}
3.threaddialog.h
#ifndef THREADDIALOG_H
#define THREADDIALOG_H
#include <QMainWindow>
#include <QDialog>
#include <QPushButton>
#include "mythread.h"
class ThreadDialog : public QDialog
{
Q_OBJECT
public:
ThreadDialog(QWidget *parent = 0);
~ThreadDialog();
protected:
void closeEvent(QCloseEvent *event);
private slots:
void startOrStopThreadA();
void startOrStopThreadB();
private:
MyThread m_ThreadA;
MyThread m_ThreadB;
QPushButton *m_ThreadAButton;
QPushButton *m_ThreadBButton;
QPushButton *m_QuitButton;
};
#endif // THREADDIALOG_H
4.threaddialog.cpp
#include "threaddialog.h"
#include <QHBoxLayout>
#include <QPushButton>
#include <QCloseEvent>
#include <QDebug>
ThreadDialog::ThreadDialog(QWidget *parent)
: QDialog(parent)
{
m_ThreadA.setMessage("A");
m_ThreadB.setMessage("B");
m_ThreadAButton = new QPushButton;
m_ThreadAButton->setText("Sart A");
m_ThreadBButton = new QPushButton;
m_ThreadBButton->setText(tr("Start B"));
m_QuitButton = new QPushButton;
m_QuitButton->setText(tr("Quit"));
m_QuitButton->setDefault(true);
connect(m_ThreadAButton,SIGNAL(clicked(bool)),SLOT(startOrStopThreadA()));
connect(m_ThreadBButton,SIGNAL(clicked(bool)),SLOT(startOrStopThreadB()));
connect(m_QuitButton,SIGNAL(clicked(bool)),SLOT(close()));
QHBoxLayout *hLayout = new QHBoxLayout(this);
hLayout->addWidget(m_ThreadAButton);
hLayout->addWidget(m_ThreadBButton);
hLayout->addWidget(m_QuitButton);
setLayout(hLayout);
setWindowTitle(tr("Thread"));
}
ThreadDialog::~ThreadDialog()
{
}
void ThreadDialog::closeEvent(QCloseEvent *event)
{
m_ThreadA.stop();
m_ThreadB.stop();
m_ThreadA.wait();
m_ThreadB.wait();
event->accept();
}
void ThreadDialog::startOrStopThreadA()
{
if(m_ThreadA.isRunning())
{
m_ThreadA.stop();
m_ThreadAButton->setText(tr("Start A"));
qDebug()<<QString::fromLocal8Bit("线程停止");
}
else
{
m_ThreadA.start();
m_ThreadAButton->setText(tr("Stop A"));
}
}
void ThreadDialog::startOrStopThreadB()
{
if(m_ThreadB.isRunning())
{
m_ThreadB.stop();
m_ThreadBButton->setText(tr("Start B"));
qDebug()<<QString::fromLocal8Bit("线程停止");
}
else
{
m_ThreadB.start();
m_ThreadBButton->setText(tr("Stop B"));
}
}
完整代码工程可在博客资源中下载:
https://download.csdn.net/download/xieliru/88845664?spm=1001.2014.3001.5501
4.运行结果
点击Start A按钮开始打印字符“A”,同时按钮显示Stop A;点击Start B按钮开始打印字符“B”,同时按钮显示Stop B;
点击Stop A按钮停止字符“A”打印,点击Stop B按钮停止字符“B”打印,点击Quit按钮退出Tread窗体。