从零开始学Qt(79):基于互斥量的线程同步
liebian365 2024-10-17 14:01 24 浏览 0 评论
QMutex和QMutexLocker是基于互斥量的线程同步类,QMutex定义的实例是一个互斥量, QMutex主要提供3个函数。
- lock():锁定互斥量,如果另外一个线程锁定了这个互斥量,它将阻塞执行直到其他线程解锁这个互斥量。
- unlock():解锁一个互斥量,需要与lock()配对使用。
- tryLock():试图锁定一个互斥量,如果成功锁定就返回true;如果其他线程己经锁定了这个互斥量,就返回false,但不阻塞程序执行。
基于QMutex的类定义
使用互斥量,对QDiceThread类重新定义(见从零开始学Qt(77):多线程程序实例 - 投骰子),不采用信号与槽机制,而是提供一个函数用于主线程读取数据。更改后的QDiceThread类定义如下:
class QDiceThread : public QThread
{
Q_OBJECT
public:
QDiceThread();
void diceBegin(); //掷一次骰子
void dicePause(); //暂停
void stopThread(); //结束线程
bool readValue(int *seq, int *diceValue); //用于主线程读取数据的函数
protected:
void run() override; //线程任务
private:
QMutex mutex; //互斥量
int m_seq=0; //掷骰子次数序号
int m_diceValue; //骰子点数
bool m_Paused=true; //暂停
bool m_stop=false; //停止
};
QDiceThread类里用QMutex类定义了一个互斥量变量mutex。
定义了函数readValue(),用于外部线程读取掷骰子的次数和点数,传递参数采用指针变量, 以便一次读取两个数据。
基于QMutex的类实现
下面是QDiceThread类中关键的run()和readValue()函数的实现代码。
void QDiceThread::run()
{//线程任务
m_stop=false; //启动线程时令 m_stop=false
m_seq=0; //掷骰子次数
qsrand(QTime::currentTime().msec());//随机数初始化,qsrand 是线程安全的
while(!m_stop) //循环主体
{
if(!m_Paused)
{
mutex.lock();
m_diceValue=qrand(); //获取随机数
m_diceValue=(m_diceValue%6)+1;
m_seq++;
mutex.unlock();
}
msleep(500); //线程休眠 500ms
}
quit();//相当于exit(0),退出线程的事件循环
}
bool QDiceThread::readValue(int *seq, int *diceValue)
{
if(mutex.tryLock()){
*seq=m_seq;
*diceValue=m_diceValue;
mutex.unlock();
return true;
}
else
return false;
}
在run()函数中,对重新计算骰子点数和掷骰子次数的3行代码用互斥量mutex的lock()和 unlock()进行了保护,这部分代码的执行就不会被其他线程中断。注意,lock()与unlock()必须配对使用。
在readValue()函数中,用互斥量mutex的tryLock()和unlock()进行了保护。如果tryLock()成功锁定互斥量,读取数值的两行代码执行时不会被中断,执行完后解锁;如果tryLock()锁定失败,函数就立即返回,而不会等待。
原理上,对于两个或多个线程可能会同时读或写的变量应该使用互斥量进行保护,例如 QDiceThread中的变量m_stop和m_paused,在run()函数中读取这两个变量,要在diceBegin()、diceEnd()和stopThread()函数里修改这些值,但是这3个函数都只有一条赋值语句,可以认为是原子操作,所以,可以不用锁定保护。
定义的互斥量mutex相当于一个标牌,可以这样来理解互斥量:列车上的卫生间一次只能进一个人,当一个人尝试进入卫生间就是lock(),如果有人占用,他就只能等待;等里面的人出来,腾出了卫生间是unlock(),这个等待的人才可以进入并且锁住卫生间的门,就是lock(),使用完卫生间之后他再出来时就是unlock()。
QMutex需要配对使用lock()和unlock()来实现代码段的保护,在一些逻辑复杂的代码段或可 能发生异常的代码中,配对就可能出错。
主线程调用子线程
这样定义的QDiceThread类,在主程序中只能调用其readValue()函数来不断读取数值。实例采用QMutex进行线程同步,其界面本系列上一篇文章中的完全相同,只是增加了定时器,用于定时主动去读取掷骰子线程的数值。 实例程序的Widget类的主要定义如下(省略了一些系统生成的声明):
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
protected:
void closeEvent(QCloseEvent *event);
private:
Ui::Widget *ui;
QDiceThread threadA;
QTimer mTimer;
int mSeq,mDiceValue;
private slots:
//自定义槽函数
void on_threadA_started();
void on_threadA_finished();
void onTimeOut(); //定期器处理槽函数
};
主要是增加了一个定时器mTimer和其时间溢出响应槽函数onTimeOut(),在Widget的构造函数中将mTimer的timeout信号与此槽函数关联。
connect(&mTimer,SIGNAL(timeout()),this,SLOT(onTimeOut()));
onTimeOut()函数的主要功能是调用threadA的readValue()函数读取数值。定时器的定时周期设置为100ms,小于threadA产生一次新数据的周期(500ms),所以可能读出旧的数据,通过存储的掷骰子的次数与读取的掷骰子次数是否不同,判断是否为新数据。onTimeOut()函数的代码如下:
void Widget::onTimeOut()
{//定时器溢出处理槽函数
int tmpSeq=0,tmpValue=0;
bool valid=threadA.readValue(&tmpSeq, &tmpValue); //读取数值
if(valid && (tmpSeq != mSeq) )//有效,并且是新数据
{
mSeq=tmpSeq;
mDiceValue=tmpValue;
QString str=QString::asprintf("第 %d 次掷散子,数为:%d", mSeq, mDiceValue);
ui->plainTextEdit->appendPlainText(str);
QPixmap pic; //图片显示
QString filename=QString::asprintf(":/dice/pic/d%d.jpg", mDiceValue);
pic.load(filename);
ui->labPic->setPixmap(pic);
}
}
窗口上几个按钮的代码如下(省略了按钮使能控制的代码):
void Widget::on_btnStartThread_clicked()
{//启动线程按钮
mSeq=0;
threadA.start();
}
void Widget::on_btnStopThread_clicked()
{//结束线程按钮
threadA.stopThread(); //结束线程的 run ()函数执行
threadA.wait();
}
void Widget::on_btnStartDice_clicked()
{//开始掷骰子按钮
threadA.diceBegin();
mTimer.start(100); //定时器 100ms 读取一次数据
}
void Widget::on_btnStopDice_clicked()
{//暂停掷骰子按钮
threadA.dicePause();
mTimer.stop(); //定时器暂停
}
基于QMutexLocker的类实现
QMutexLocker是另外一个简化了互斥量处理的类。QMutexLocker的构造函数接受一个互斥量作为参数并将其锁定,QMutexLocker的析构函数则将此互斥量解锁,所以在QMutexLocker实例变量的生存期内的代码段得到保护,自动进行互斥量的锁定和解锁。例如,QDiceThread的run() 函数的代码可以改写如下:
void QDiceThread::run()
{//线程任务
m_stop=false; //启动线程时令 m_stop=false
m_seq=0; //掷骰子次数
qsrand(QTime::currentTime().msec());//随机数初始化,qsrand 是线程安全的
while(!m_stop) //循环主体
{
if(!m_Paused)
{
QMutexLocker Locker(&mutex);
m_diceValue=qrand(); //获取随机数
m_diceValue=(m_diceValue%6)+1;
m_seq++;
}
msleep(500); //线程休眠 500ms
}
quit();//相当于exit(0),退出线程的事件循环
}
其它与前面的相同,程序实现完全相同的功能。
————————————————
觉得有用的话请关注点赞,谢谢您的支持!
对于本系列文章相关示例完整代码有需要的朋友,可关注并在评论区留言!
相关推荐
- 4万多吨豪华游轮遇险 竟是因为这个原因……
-
(观察者网讯)4.7万吨豪华游轮搁浅,竟是因为油量太低?据观察者网此前报道,挪威游轮“维京天空”号上周六(23日)在挪威近海发生引擎故障搁浅。船上载有1300多人,其中28人受伤住院。经过数天的调...
- “菜鸟黑客”必用兵器之“渗透测试篇二”
-
"菜鸟黑客"必用兵器之"渗透测试篇二"上篇文章主要针对伙伴们对"渗透测试"应该如何学习?"渗透测试"的基本流程?本篇文章继续上次的分享,接着介绍一下黑客们常用的渗透测试工具有哪些?以及用实验环境让大家...
- 科幻春晚丨《震动羽翼说“Hello”》两万年星间飞行,探测器对地球的最终告白
-
作者|藤井太洋译者|祝力新【编者按】2021年科幻春晚的最后一篇小说,来自大家喜爱的日本科幻作家藤井太洋。小说将视角放在一颗太空探测器上,延续了他一贯的浪漫风格。...
- 麦子陪你做作业(二):KEGG通路数据库的正确打开姿势
-
作者:麦子KEGG是通路数据库中最庞大的,涵盖基因组网络信息,主要注释基因的功能和调控关系。当我们选到了合适的候选分子,单变量研究也已做完,接着研究机制的时便可使用到它。你需要了解你的分子目前已有哪些...
- 知存科技王绍迪:突破存储墙瓶颈,详解存算一体架构优势
-
智东西(公众号:zhidxcom)编辑|韦世玮智东西6月5日消息,近日,在落幕不久的GTIC2021嵌入式AI创新峰会上,知存科技CEO王绍迪博士以《存算一体AI芯片:AIoT设备的算力新选择》...
- 每日新闻播报(September 14)_每日新闻播报英文
-
AnOscarstatuestandscoveredwithplasticduringpreparationsleadinguptothe87thAcademyAward...
- 香港新巴城巴开放实时到站数据 供科技界研发使用
-
中新网3月22日电据香港《明报》报道,香港特区政府致力推动智慧城市,鼓励公私营机构开放数据,以便科技界研发使用。香港运输署21日与新巴及城巴(两巴)公司签署谅解备忘录,两巴将于2019年第3季度,开...
- 5款不容错过的APP: Red Bull Alert,Flipagram,WifiMapper
-
本周有不少非常出色的app推出,鸵鸟电台做了一个小合集。亮相本周榜单的有WifiMapper's安卓版的app,其中包含了RedBull的一款新型闹钟,还有一款可爱的怪物主题益智游戏。一起来看看我...
- Qt动画效果展示_qt显示图片
-
今天在这篇博文中,主要实践Qt动画,做一个实例来讲解Qt动画使用,其界面如下图所示(由于没有录制为gif动画图片,所以请各位下载查看效果):该程序使用应用程序单窗口,主窗口继承于QMainWindow...
- 如何从0到1设计实现一门自己的脚本语言
-
作者:dong...
- 三年级语文上册 仿写句子 需要的直接下载打印吧
-
描写秋天的好句好段1.秋天来了,山野变成了美丽的图画。苹果露出红红的脸庞,梨树挂起金黄的灯笼,高粱举起了燃烧的火把。大雁在天空一会儿写“人”字,一会儿写“一”字。2.花园里,菊花争奇斗艳,红的似火,粉...
- C++|那些一看就很简洁、优雅、经典的小代码段
-
目录0等概率随机洗牌:1大小写转换2字符串复制...
- 二年级上册语文必考句子仿写,家长打印,孩子照着练
-
二年级上册语文必考句子仿写,家长打印,孩子照着练。具体如下:...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- wireshark怎么抓包 (75)
- qt sleep (64)
- cs1.6指令代码大全 (55)
- factory-method (60)
- sqlite3_bind_blob (52)
- hibernate update (63)
- c++ base64 (70)
- nc 命令 (52)
- wm_close (51)
- epollin (51)
- sqlca.sqlcode (57)
- lua ipairs (60)
- tv_usec (64)
- 命令行进入文件夹 (53)
- postgresql array (57)
- statfs函数 (57)
- .project文件 (54)
- lua require (56)
- for_each (67)
- c#工厂模式 (57)
- wxsqlite3 (66)
- dmesg -c (58)
- fopen参数 (53)
- tar -zxvf -c (55)
- 速递查询 (52)