系列文章目录
文章目录
- 系列文章目录
- 前言
- 多线程操作
- 多线程创建
- 基本概念
- 接口函数
- 线程类的定义实例
- 线程同步
- 基础互斥量的线程同步
- 基于QReadWriteLock的线程同步
- 基于QWaitCondition的线程同步
- 基于信号量的线程同步
- 总结
前言
由于目前时间比较赶,同时还在学习FreeRTOS,可能没有很多时间详细学习Qt了,所以结合需要编写的上位机,先暂时跳过一些基础知识,挑一些重要的知识点先学会,其他的之后用到了再一边学一边补充吧。
多线程操作
为了防止某些操作消耗时间较长,导致其他程序无法响应,所以出现了多线程操作,需要处理好同步和数据交互。
多线程运行方式:
1.Qt的主线程是窗口线程:负责窗口事件的处理和窗口控件的更新
2.子线程(工作线程)负责后台的业务逻辑,不能对窗口对象操作
3.他们之间一般使用信号与槽完成数据的传递,当然也有一些其他的同步方式
一般Qt的多线程操作用的是QThread类。
线程之间同步:QMutex、QMutexLocker、QReadWritelock、QwaitCondition、Qsemaphore。
使用Qt Concurrent实现的多线程可以自动根据处理器内核个数调整线程个数,而且不用考虑对共享数据的保护问题。
多线程创建
网上搜索有很多的创建方式,我先总结一下利用QThread类的方式创建。
基本概念
一个QThread管理一个线程,一般我们自定义一个继承QThread的类,重定义虚函数run(),该函数里需要完成的线程任务。
一般在主线程创建线程,调用start()开始执行任务,其内部会自动调用run() ,开始任务循环执行。
在run() 函数里调用exit()或quit()结束线程,或在主线程调用terminate() 强制结束线程。
接口函数
//公共函数:
bool isFinished() //线程是否结束
bool isRunning() //线程是否运行
Priority priority() //返回线程优先级
void setPriority(Priority priority) //设置线程优先级
void exit(int returnCode=0) //退出线程的事件循环,退出码为returnCode,0成功,否则出错
bool wait(unsigned long time) //阻止线程执行,使之等待time毫秒(之后继续运行或者结束)
//公共槽函数
void quit() //退出线程,返回代码0,等效exit(0)
void start(Priority priority) //其内部调用run() 开始线程,操作系统根据priority调度
void terminate() //终止线程运行,但不是立刻结束,而是等待操作系统结束,之后应该加上wait()
//信号
void finished() //在线程结束时发射此信号
void start() //在线程开始的时候执行,run()函数被调用之前发射此信号
//静态公共成员
int idealThreadCount() //返回系统上能运行线程的理想个数
void msleep(unsigned long msecs) //强制休眠msecs毫秒
void sleep(unsigned long secs) //强制休眠secs秒
void usleep(unsigned long usecs) //强制休眠usecs微秒
//保护函数
virtual void run() //run() 被调用开始线程任务的执行,所以在run()里面实现线程的任务
int exec() //有run()函数调用,进入线程的事件循环,等待exit()退出
线程类的定义实例
头文件实例:
#include <Qthread>
class QDiceThread : public Qthread
{
o_OBJECT
private : //仅类内函数可以访问(继承也不行)
int m_seq=0 ;//掷骰子次数序号
int m_dicevalue;//骰子点数
bool m_Paused=true;//暂停
bool m_stop=false;//停止
protected: //类内及继承可以访问
void run() o_DECL_OVERRIDE;//线程任务,需要重定义
public: //所有都能访问
QDiceThread () ;
void diceBegin () ; //掷一次骰子
void dicePause () ;//暂停
void stopThread ( ) ;//结束线程
signals :
void newvalue(int seq,int dicevalue) ; //产生新点数
};
之后正常定义好线程的各部分函数,在主线程中启动等操作,或者控件中启动。
在窗口关闭时我们要确保所有的线程停止:
void Dialog : : closeEvent (QcloseEvent *event)
{
//窗口关闭事件,必须结束线程
if (threadA. isRunning())
{
threadA.stopThread();
threadA. wait();
}
event->accept() ; //关闭窗口,ignore()不关闭
}
重定义run():
void QDiceThread: : run()
{//线程任务
m_stop=false;//启动线程时令m_stop=false
m_seq=0 ; //掷骰子次数
qsrand (Qtime::currentTime().msec());//随机数初始化,qsrand是线程安全的
while (!m stop)//循环主体
{
if (!m_Paused)
{
m_dicevalue=qrand ( ); //获取随机数
m_dicevalue=(m_dicevalue % 6)+1;
m_seq++;
emit newvalue(m_seq,m_dicevalue);//发射信号
msleep (500) ; //线程休眠500ms
}
quit( );//相当于exit (o),退出线程的事件循环
}
主要是通过信号与槽机制实现的,这里主线程会等待工作线程的信号通知,所以不涉及到线程之间变量同时读写的问题。
线程同步
如果不使用信号与槽机制,由于线程之间可能要同时访问一个变量,如果这个变量正在写入的时候读取,就会出现错误。因此出现了几种方法:
基础互斥量的线程同步
QMutex和QMutexLocker的类,创建互斥量,对它进行lock(),unlock(),tryLock(),在写入读出的时候,进行锁定,读写完成的时候解锁。
有些函数虽然涉及到变量的读写,但是如果只有一条语句,相当原子语句,不需要锁定保护
QMutexLocker简化了一些,接收一个互斥量进行构建并锁定,只有在其生命周期结束的时候才会解锁。
基于QReadWriteLock的线程同步
很明显,上面的方法,当同时有多个线程读取的时候,是不需要锁定的。
基于QWaitCondition的线程同步
前面为了避免同时访问资源时发生冲突。在一个线程解锁资源后,不能及时通知其他线程。
基于信号量的线程同步
总结
先简单了解一下,之后用到什么再补充吧