同步线程
↵
由于这节内容资料比较少,所以以下内容总结自Qt官方文献,在文章最后会给出相应链接。
线程的目的是允许并行运行,但有时线程必须停止等待其他线程。例如,如果两个线程尝试访问同一个变量,这样的话结果是未定义的。强制线程相互等待的原则成为互斥,是一种保护共享资源的常用技术。
同步线程类:
- QMutex 互斥锁
- QReadWriteLock 读-写锁
- QSemaphore 信号量
- QWaitCondition 条件变量
QMutex(互斥锁)
提供一个互斥锁,在任何事件至多有一个线程可以获得mutex。如果一个线程尝试获得mutex,而mutex已经锁住,那么这个线程将会睡眠
QReadWriteLock (读-写锁)
读-写锁,于QMutex相似,但它对共享数据进行访问的区分,分为“读”,“写”访问,允许多个线程同时对数据进行“读”访问,QReadWiteLock的并行度大于QMutex
QSemaphore 信号量
QSemaphore是QMutex的概括,可保护一定数量的相同资源。相比之下,QMutex 只保护一种资源。信号量示例显示了信号量的典型应用:同步对生产者和使用者之间循环缓冲区的访问
QWaitCondition 条件变量
QWaitCondition,允许一个线程在一些条件满足时唤醒其他线程, 不是通过强制执行互斥而是通过提供条件变量来同步线程。一个或多个线程可以被堵塞来等待一个QWaitCondition,使用wakeOne()可以唤醒一个随机选取的等待的线程,使用wakeAll()可以唤醒所有正在等待的线程
QMutex
QMutex通常和QMutexLocker一起使用,才可以确保这可以轻松确保锁定和解锁的执行一致。
常用函数:
| isRecursive() | 判断互斥锁是否为递归(Qt 5.7引入) |
| lock() | 锁定互斥锁,如果另一个线程锁定了互斥锁,则此调用将阻塞,直到该线程解锁它 |
| tryLock(int ) | 尝试锁定互斥锁,如果已获得锁,则函数返回true 否则返回 false,可以设置等待时间。如果获得锁则必须使用unlock()解锁互斥锁,这样才能在另一个线程才能成功锁定它。 |
| try_lock() | 尝试锁定互斥锁,如果已获得锁,则函数返回true 否则返回 false,提供此功能是为了与标准库概念兼容,等价于tryLock()(Qt 5.8 引入) |
| try_lock_for() | 尝试锁定互斥锁,如果已获得锁,则函数返回true 否则返回 false, 如果获得锁,必须使用unlock()解锁互斥锁 如果为递归互斥锁,允许在同一线程的容易个互斥锁上多次调用该函数 如果此互斥锁是非递归互斥锁,则在尝试递归锁定互斥锁时,此函数将始终返回 false。(Qt 5.8 引入) |
| try_lock_until() | 尝试锁定互斥锁,如果已获得锁,则函数返回true 否则返回 false, 如果获得锁,必须使用unlock()解锁互斥锁 如果为递归互斥锁,允许在同一线程的容易个互斥锁上多次调用该函数 如果此互斥锁是非递归互斥锁,则在尝试递归锁定互斥锁时,此函数将始终返回 false。(Qt 5.8 引入) |
| unlock() | 解锁互斥锁。 |
QMutex::QMutex(QMutex::RecursionMode mode)
创建一个互斥锁时可以设置模式
| QMutex::Recursive | 在这种模式下,线程可以多次锁定同一个互斥锁,并且在进行相应数量的 unlock() 调用之前,互斥锁不会被解锁 |
| QMutex::NonRecursive | 在此模式下,线程只能锁定一次互斥锁 |
互斥锁使用场景:当一个变量同时被多个线程访问
int number=10;void text()
{number*=2;number+=5;
}
void text1()
{number+=10;number*3;
}
正常调用 text()和text1()的话
text() number=2*10=20+5=25
text1() number=25+10=30*3=90
同时调用的话:可能会发生以下情况
线程1调用text()
number=10*2=20;
线程2调用text1(),现在text()的调用暂停
number=20+10=30
number=30*3=90;
继续完成线程text()
number=90+5=95
为了防止以上情况,可以上个互斥锁,使得调用完某个函数才能调用其他函数。
QMutex mutex;//互斥锁int number=10;void text()
{mutex.lock();//上锁number*=2;number+=5;mutex.unlock();//解锁
}void text1()
{mutex.lock();//上锁number+=10;number*=3;mutex.unlock();//解锁
}
QMutexLocker类
QMutexLocker类是一个方便点的类,可以简化锁定和解锁的互斥锁。
使用方法:QMutexLocker应该在需要锁定QMutex的函数中创建,创建一个QMutexLock时,互斥锁被锁定。(更加方便)
函数:
| mutex(() | 返回正在运行的互斥锁 |
| relock() | 重新锁定未锁定的互斥锁锁。 |
| unlock() | 解锁此互斥锁 |
使用QMutex的情况:需要在分支中解锁互斥锁
int complexFunction(int flag)
{mutex.lock();int retVal = 0;switch (flag) {case 0:case 1:retVal = moreComplexFunction(flag);break;case 2:{int status = anotherFunction();if (status < 0) {mutex.unlock();return -2;}retVal = status + flag;}break;default:if (flag > 10) {mutex.unlock();return -1;}break;}mutex.unlock();return retVal;
}
QMutexLocker的使用
int complexFunction(int flag)
{QMutexLocker locker(&mutex);//创建一个QmutexLocker对象int retVal = 0;switch (flag) {case 0:case 1:return moreComplexFunction(flag);case 2:{int status = anotherFunction();if (status < 0)return -2;retVal = status + flag;}break;default:if (flag > 10)return -1;break;}return retVal;
}
当函数执行完后,QMutexLocker对象销毁时,互斥锁会解锁,就不用每个分支去解锁。
使用QMutexLocker::mutex()可以获取当前正在运行的互斥锁。
QReadWriteLock(读-写锁)
读写锁是一种同步工具,用于保护可以访问以进行读取和写入的资源。如果要允许多个线程同时具有只读访问权限,则这种类型的锁定很有用,但是一旦一个线程想要写入资源,就必须阻止所有其他线程,直到写入完成。
函数:
| lockForRead() | 锁定读取, 如果另一个线程已锁定写入,此函数将阻止当前线程。 如果线程已锁定写入,则无法锁定读取。 |
| lockForWrite() | 锁定写入 如果另一个线程(包括当前线程)已锁定以进行读取或写入,则此函数将阻止当前线程 |
| tryLockForRead() | 尝试锁定以进行读取,如果线程已锁定写入,则无法锁定读取 |
| tryLockForWrite() | 尝试锁定以进行写入,如果线程已锁定以进行读取,则无法锁定写入 |
| unlock() | 解锁 |
QReadWriteLock通常使用于经常读取数据,可以多个线程同时读取数据。
QReadWriteLock lock;void ReaderThread::run()
{...lock.lockForRead();read_file();lock.unlock();...
}void WriterThread::run()
{...lock.lockForWrite();write_file();lock.unlock();...
}
QSemaphore(信号量)
信号量是互斥锁的概括。虽然互斥锁只能锁定一次,但可以多次获取信号量。信号量通常用于保护一定数量的相同资源
函数:
| acquire(int n) | 尝试获取n个由信号量保护的资源,当资源不够时将堵塞直到资源足够。 |
| release(int n) | 释放由信号量保护的 n 个资源, 此函数也可用于“创建”资源。 |
| available() | 返回信号灯当前可用的资源数。这个数字永远不能是负数。 |
| tryAcquire(int n) | 尝试获取由信号量保护的资源,成功返回true,否则返回false |
| tryAcquire(int n,int timeout ) | 尝试获取由信号量保护的资源,成功返回true,否则返回false,调用最多等待timeout秒 |
QSemaphore的创建
QSemaphore::QSemaphore(int n=0)
创建新的信号量,并将其保护的资源数初始化为 n(默认为 0)。
QSemaphore sem(5);sem.acquire(3);qDebug()<<"资源还有"<<sem.available()<<"个";sem.acquire(2);qDebug()<<"资源还有"<<sem.available()<<"个";sem.release(5);qDebug()<<"资源还有"<<sem.available()<<"个";

当释放的资源多余需要释放的资源时,多余的会进行创建
QSemaphore sem(5);sem.acquire(3);qDebug()<<"资源还有"<<sem.available()<<"个";sem.release(5);qDebug()<<"资源还有"<<sem.available()<<"个";sem.release(10);qDebug()<<"资源还有"<<sem.available()<<"个";

当资源少于需要获取的资源时,不会获取成功。
QSemaphore sem(5);if(sem.tryAcquire(7)){qDebug()<<"获取成功";}else{qDebug()<<"获取失败";}
QWaitCondition
允许一个线程在一些条件满足时唤醒其他线程, 不是通过强制执行互斥而是通过提供条件变量来同步线程。一个或多个线程可以被堵塞来等待一个QWaitCondition,使用wakeOne()可以唤醒一个随机选取的等待的线程,使用wakeAll()可以唤醒所有正在等待的线程
函数:
| wait(QMutex*,time) | 释放锁定的互斥锁并等待等待条件 |
| wakeOne() | 唤醒一个等待等待条件的线程。唤醒的线程取决于操作系统的调度策略,无法控制或预测。如果要唤醒特定线程,解决方案通常是使用不同的等待条件,并让不同的线程等待不同的条件 |
| wakeAll() | 唤醒等待等待条件的所有线程。线程的唤醒顺序取决于操作系统的调度策略,无法控制或预测。 |
| notify_one() | 提供此函数是为了与 STL 兼容。它等效于 wakeOne()Qt 5.8中引入 |
| notify_all() | 提供此函数是为了与 STL 兼容。它等效于 wakeAll()Qt 5.8中引入 |
QWaitCondition的示例:
使用QWaitCondition的QMutex解决生产者-消费者问题
设置全局变量:
const int DataSize = 100000;//生产者将生成的数据量const int BufferSize = 8192;//缓冲区
char buffer[BufferSize];//两个等待条件,一个互斥锁和计数器
QWaitCondition bufferNotEmpty;
QWaitCondition bufferNotFull;
QMutex mutex;
int numUsedBytes = 0;
生产者类:
class Producer : public QThread
{
public:Producer(QObject *parent = NULL) : QThread(parent){}void run() override{for (int i = 0; i < DataSize; ++i) {mutex.lock();//上锁if (numUsedBytes == BufferSize)//检查缓冲区是否已满bufferNotFull.wait(&mutex);//已满的话等待条件满足mutex.unlock();//解锁//存放数据(随机数)buffer[i % BufferSize] = "ACGT"[QRandomGenerator::global()->bounded(4)];mutex.lock();//上锁++numUsedBytes;计数器+1bufferNotEmpty.wakeAll();//唤醒全部线程mutex.unlock();//解锁}}
};
消费者类:
class Consumer : public QThread
{Q_OBJECT
public:Consumer(QObject *parent = NULL) : QThread(parent){}void run() override{for (int i = 0; i < DataSize; ++i) {mutex.lock();if (numUsedBytes == 0)//检查缓冲区是否为空bufferNotEmpty.wait(&mutex);mutex.unlock();fprintf(stderr, "%c", buffer[i % BufferSize]);//输出内容mutex.lock();--numUsedBytes;//计数器-1bufferNotFull.wakeAll();mutex.unlock();}fprintf(stderr, "\n");}signals:void stringConsumed(const QString &text);
};
main函数:
int main(int argc, char *argv[])
{QCoreApplication app(argc, argv);Producer producer;//生产者Consumer consumer;//消费者producer.start();//开启线程consumer.start();//开启线程//两个线程调用wait(),阻塞线程,确保两个线程在退出前都有时间能完成main()producer.wait();consumer.wait();return 0;
}
QSemaphore信号量的生产者消费者问题。
全局变量 :
const int DataSize=1000;//生产者的数据量
const int BufferSize=800;//缓冲区大小
char buffer[Buffersize];
QSemaphore freeBytes(BufferSize);//控制缓冲区的信号量
QSemaphore usedBytes;//控制已经使用的缓冲区
生产者类:
class Producer :public QThread
{
public:void run();
}void Producer::run()
{qsrand(QTime(0.0.0).secsTo(QTime::currentTime()));//随机数for(int i=0;i<DataSize;++i){freeBytes.acquire();buffer[%BufferSize]="ACGT"[(int)qrand %4];qDebug()<<QString("Producer:%1").arg(buffer[i%buffersize]);usedBytes.release();}
消费者类:
class Consumer :public QThread
{
public:void run();
}void Producer::run()
{for(int i=0;i<DataSize;++i){userBytes.acquire();qDebug()<<QString("Producer:%1").arg(buffer[i%buffersize]);freeBytes.release();}}
main()
int main(int argc, char *argv[])
{QCoreApplication app(argc, argv);Producer producer;//生产者Consumer consumer;//消费者producer.start();//开启线程consumer.start();//开启线程//两个线程调用wait(),阻塞线程,确保两个线程在退出前都有时间能完成main()producer.wait();consumer.wait();return 0;
}
参考文献:
同步线程|Qt 5.15
QMutex 类 |Qt核心 5.15.12
QMutexLocker Class |Qt核心 5.15.12
QReadWriteLock 类 |Qt核心 5.15.12
QSemaphore Class | Qt Core 5.15.12
QWaitCondition Class | Qt Core 5.15.12
相关文章:
同步线程
↵ 由于这节内容资料比较少,所以以下内容总结自Qt官方文献,在文章最后会给出相应链接。 线程的目的是允许并行运行,但有时线程必须停止等待其他线程。例如,如果两个线程尝试访问同一个变量,这样的话结果是未定义的。强…...
服务端返回内容跨域CORS之后,也在chrome/edge浏览器里显示出响应信息
由于浏览器的同源策略,服务端返回的内容跨域,且没有允许跨域CORS的请求头之后,浏览器无法显示出服务端返回的信息,不方便问题排查。比如:Access to XMLHttpRequest at http://localhost:6001/service-app/query/common…...
DHCP中继及配置
为什么需要DHCP Relay?产生背景解决方案DHCP Relay工作原理DHCP Relay配置实现产生背景 随着网络规模的扩大,网络中就会出现用户处于不同网段的情况。 这个时候客户A和客户B要请求IP地址时,首先会发送DHCP Discover广播包,这个广…...
中国社科院与美国杜兰大学金融管理硕士,让我们相遇在春暖花开时
在芸芸众生中,能拥有志同道合的朋友是一件多么幸运的事。人们常说:你是谁,就会遇见谁。走过半生才知道,看似命中注定的遇见谁、发生的事,其实都取决于自己。只有自己足够优秀,才能遇到更优秀的别人。在这个…...
MySQL---单表查询、多表查询
一、单表查询 素材: 表名:worker-- 表中字段均为中文,比如 部门号 工资 职工号 参加工作 等 CREATE TABLE worker ( 部门号 int(11) NOT NULL, 职工号 int(11) NOT NULL, 工作时间 date NOT NULL, 工资 float(8,2) NOT NULL, 政治面貌 v…...
3年自动化测试这水平?我还不如去招应届生
公司前段缺人,也面了不少测试,结果竟然没有一个合适的。一开始瞄准的就是中级的水准,也没指望来大牛,提供的薪资在10-20k,面试的人很多,但平均水平很让人失望。看简历很多都是3年工作经验,但面试…...
5 个自定义 React Hooks 将改变你的代码
昨天完成我的每日文章(是的,我每天都会发布一篇关于前端开发的新文章,所以如果你想要每天的代码丸,请务必关注 😉),我去编码了一点......我开始为我正在构建的副项目编写一些自定义挂钩…...
Java学习笔记-03(API阶段)
前言 目前我们看到的是Java基础部分的一个新的部分API,这是个啥,又能做啥呢? 其实可以概括成一句话:帮助我们站在巨人的肩膀上,实现更加高效的开发,那么我们来一探究竟吧~ API API(Application Programming Interface,应用程序接口)是一些预…...
Django自定义模板标签的使用详解
目录 1.创建子应用:python manage.py startapp test01 2.进行相关的配置 3.在新建的test01文件下创建urls.py(此处名称可变但注意上图) 4.在test01文件下创建名称为templatetags的文件夹 5.templatetags文件下继续创建几个py文件如下图编辑 6.views视图函数…...
洗地机怎么选?洗地机品牌排行榜
洗地机的出现不仅能高效的清洁地面还能节省我们做家务的时间,对于上班族、有宠物的家庭以及宝妈来说简直不要太方便;目前市面上的洗地机有分有线款和无线款,无线款会比有线款操作更加方便;洗地机怎么选,其实洗地机的清洁能力主要是看吸力大小…...
CSS的元素显示模式
😊博主页面:鱿年年 👉博主推荐专栏:《WEB前端》👈 💓博主格言:追风赶月莫停留,平芜尽处是春山❤️ 目录 前言 一、什么是元素显示模式 1.1块元素 1.2行内元素 1.3行内块元素…...
【MySQL Shell】8.9.1 在 InnoDB ClusterSet 中隔离集群
在发生紧急故障切换后,如果 ClusterSet 的各个部分之间存在事务集不同的风险,则必须保护集群不受写入流量或所有流量的影响。 如果发生网络分区,则有可能出现脑裂的情况,即实例失去同步,无法正确通信以定义同步状态。…...
Ubuntu20.04+cuda11.2+cudnn8.1+Anaconda3安装tensorflow-GPU环境,亲测可用
(1)安装nvidia显卡驱动注意Ubuntu20.04和Ubuntu16.04版本的安装方法不同,安装驱动前一定要更新软件列表和安装必要软件、依赖(必须)sudo apt-get update #更新软件列表sudo apt-get install gsudo apt-get install gccsudo apt-get install make查看GP…...
剑指Offer 第27天 JZ75 字符流中第一个不重复的字符
字符流中第一个不重复的字符_牛客题霸_牛客网 描述 请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符 "go" 时,第一个只出现一次的字符是 "g" 。当从该字符流中读出前六个字符 “google&…...
科研试剂供应1476737-97-9,Bis-PEG2-endo-BCN可发生点击反应
●外观以及性质:Bis-PEG2-endo-BCN一般为白色固体,BCN其为点击试剂,点击化学(Click chemistry),又译为“链接化学”、“动态组合化学” (Dynamic Combinatorial Chemistry)、“速配接…...
Zabbix 构建监控告警平台(一)--部署安装
监控对象监控收集信息方式Zabbix 部署 1.监控对象 源代码: *.html *.jsp *.php *.py 数据库: MySQL,MariaDB,Oracle,SQL Server,DB2 应用软件:Nginx,Apache,PHP,Tomcat agent 集群: LVS,Keepalived,HAproxy…...
【nodejs】nodejs入门核心知识(命令行使用、内置模块、node 模块化开发)
💻 nodejs入门核心知识(命令行使用、内置模块、node 模块化开发) 🏠专栏:JavaScript 👀个人主页:繁星学编程🍁 🧑个人简介:一个不断提高自我的平凡人🚀 🔊分享…...
5. Spring 事务
文章目录1. Spring 事务简介2. Spring 事务角色3. Spring 事务属性3.1 事务配置3.2 案例:转账业务追加日志3.3 事务传播行为1. Spring 事务简介 Spring 事务作用:在数据层或业务层保障一系列的数据库操作同成功、同失败。 数据层有事务我们可以理解&am…...
【堆】数据结构堆的实现(万字详解)
前言: 在上一期中我们讲到了树以及二叉树的基本的概念,有了之前的认识,今天我们将来具体实现一种二叉树的存储结构“堆”!!! 目录1.二叉树顺序结构介绍2.堆的概念及结构3.调整算法3.1向上调整算法3.1.1算法…...
Docker进阶 - 9. docker network 之自定义网络
1. 运行两个tomcat实例,并进入容器内部 docker run -d -p 8081:8080 --name tomcat81 billygoo/tomcat8-jdk8 docker exec -it tomcat81 bashdocker run -d -p 8082:8080 --name tomcat82 billygoo/tomcat8-idk8 docker exec -it tomcat82 bash2. ping一下各自的ip…...
label-studio的使用教程(导入本地路径)
文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...
利用ngx_stream_return_module构建简易 TCP/UDP 响应网关
一、模块概述 ngx_stream_return_module 提供了一个极简的指令: return <value>;在收到客户端连接后,立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量(如 $time_iso8601、$remote_addr 等)&a…...
HTML 列表、表格、表单
1 列表标签 作用:布局内容排列整齐的区域 列表分类:无序列表、有序列表、定义列表。 例如: 1.1 无序列表 标签:ul 嵌套 li,ul是无序列表,li是列表条目。 注意事项: ul 标签里面只能包裹 li…...
[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?
论文网址:pdf 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误,若有发现欢迎评论指正!文章偏向于笔记,谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...
P3 QT项目----记事本(3.8)
3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...
C++.OpenGL (10/64)基础光照(Basic Lighting)
基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...
selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...
html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
uniapp 实现腾讯云IM群文件上传下载功能
UniApp 集成腾讯云IM实现群文件上传下载功能全攻略 一、功能背景与技术选型 在团队协作场景中,群文件共享是核心需求之一。本文将介绍如何基于腾讯云IMCOS,在uniapp中实现: 群内文件上传/下载文件元数据管理下载进度追踪跨平台文件预览 二…...
