同步线程

news2024/11/28 5:08:39

  

由于这节内容资料比较少,所以以下内容总结自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;计数器+1
            bufferNotEmpty.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;//计数器-1
            bufferNotFull.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 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/335719.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【0基础学爬虫】爬虫基础之爬虫的基本介绍

大数据时代&#xff0c;各行各业对数据采集的需求日益增多&#xff0c;网络爬虫的运用也更为广泛&#xff0c;越来越多的人开始学习网络爬虫这项技术&#xff0c;K哥爬虫此前已经推出不少爬虫进阶、逆向相关文章&#xff0c;为实现从易到难全方位覆盖&#xff0c;特设【0基础学…

Jmeter in Linux - 在Linux系统使用Jmeter的坑

Jmeter in Linux - 在Linux系统使用Jmeter的坑Jmeter in Linux系列目录&#xff1a;o.a.j.JMeter: Error in NonGUIDriver起因错误分析&#xff1a;解决方案&#xff1a;解析日志没有展示请求和响应信息起因解决方案&#xff1a;注意Jmeter in Linux系列目录&#xff1a; 【如…

ChatGPT 爆火!谷歌、微软、百度纷纷下场?

近日&#xff0c;智能聊天机器人ChatGPT的爆火引发了国内外网友的热烈讨论&#xff0c;上线两个月后&#xff0c;用户数量达到1亿。2月8日下午&#xff0c;巨大的访问量让系统一度崩溃。 服务重新开放后&#xff0c;我向ChatGPT询问了如何快速扩容&#xff0c;它显然是知道云端…

CSS从入门到精通专栏简介

先让我们来欣赏几个精美的网站&#xff1a; Matt Brett - Freelance Web Designer and WordPress Expert ‎2022 Year in Review • Letterboxd NIO蔚来汽车官方网站 小米官网 Silk – Interactive Generative Art 大屏数据可视化 你是否也有过这样的“烦恼”&#xff1a; * …

(C00034)基于Springboot+html前后端分离技术的宿舍管理系统-有文档

基于Springboothtml技术的宿舍管理系统-有文档项目简介项目获取开发环境项目技术运行截图项目简介 基于Springboothtml的前后端分离技术的宿舍管理系统项目为了方便对学生宿舍进行管理而设计&#xff0c;分为后勤、宿管、学生三种用户&#xff0c;后勤对整体宿舍进行管理、宿管…

第九层(16):STL终章——常用集合算法

文章目录前情回顾常用集合算法set_intersectionset_unionset_difference最后一座石碑倒下&#xff0c;爬塔结束一点废话&#x1f389;welcome&#x1f389; ✒️博主介绍&#xff1a;一名大一的智能制造专业学生&#xff0c;在学习C/C的路上会越走越远&#xff0c;后面不定期更…

JY-7A/3DK/220 19-130V静态【电压继电器】

系列型号 JY-7A/1DK不带辅助电源电压继电器&#xff1b;JY-7B/1DK不带辅助电源电压继电器&#xff1b; JY-7/1DK/120不带辅助电源电压继电器&#xff1b;JY-7/1DK/120不带辅助电源电压继电器&#xff1b; JY-7A/1DKQ不带辅助电源电压继电器&#xff1b;JY-7B/1DKQ不带辅助电源…

Python入门之ChatGPT的API调用(Python版)

一、Python环境部署 参考Python 环境搭建 | 菜鸟教程 Python官网&#xff1a;Welcome to Python.org Python文档下载地址&#xff1a;Our Documentation | Python.org 二、Thonny的安装 安装包地址&#xff1a;Thonny, Python IDE for beginners 三、ChatGPT的Key申请 网…

虚拟机安装CentOS 7(带界面)

目录 一、虚拟机安装CentOS 7&#xff08;带界面&#xff09; 1、打开下好的VMware&#xff0c;点击创建虚拟机 2、下一步 3、点击下一步 4、选择Linux&#xff0c;ContOS7&#xff0c;点击下一步 5、修改虚拟机名称和路径 6、下一步 7、点击自定义硬件 8、设置虚拟机大…

《第一行代码》 第三章:UI布局开发

一&#xff0c;常用控件的使用方法 1&#xff0c;TextView <TextViewandroid:id"id/text_view"android:layout_width"match_parent"android:layout_height"wrap_content"android:gravity"center"android:textSize"24sp"…

iptables防火墙SNAT和DNAT

iptables防火墙SNAT和DNAT一、SNAT原理和应用1、SNAT原理2、SNAT应用环境3、SNAT转换前提条件二、SNAT案列1、实验需求2、实验环境3、实验目的三、DNAT原理和应用1、DNAT原理2、DNAT 应用环境3、DNAT转换的前提条件四、DNAT案列总结一、SNAT原理和应用 1、SNAT原理 SNAT原理:…

365天深度学习训练营 第P6周:好莱坞明星识别

&#x1f368; 本文为&#x1f517;365天深度学习训练营 内部限免文章&#xff08;版权归 K同学啊 所有&#xff09;&#x1f366; 参考文章地址&#xff1a; &#x1f517;第P6周&#xff1a;好莱坞明星识别 | 365天深度学习训练营&#x1f356; 作者&#xff1a;K同学啊 | 接…

扩散模型diffusion model用于图像恢复任务详细原理 (去雨,去雾等皆可),附实现代码

文章目录1. 去噪扩散概率模型2. 前向扩散3. 反向采样3. 图像条件扩散模型4. 可以考虑改进的点5. 实现代码1. 去噪扩散概率模型 扩散模型是一类生成模型, 和生成对抗网络GAN 、变分自动编码器VAE和标准化流模型NFM等生成网络不同的是, 扩散模型在前向扩散过程中对图像逐步施加噪…

如何为自己的应用选择数据库?有这些考虑因素

节选翻译自 Michal Toiba 的博客 微软前不久宣布推出分布式关系数据库 Azure Cosmos DB for PostgreSQL&#xff0c;使 Azure 成为第一个在单一数据库服务中同时支持关系和 NoSQL&#xff08;非关系&#xff09;数据的云平台。这意味着 Azure Cosmos DB 开发者在构建云原生应用…

只需三步,完成ChatGPT微信机器人搭建

大家好&#xff0c;我是可乐。 这两天 Chatgpt 又爆火了&#xff0c;去年12月份刚出来的时候&#xff0c;我写了两篇文章&#xff1a; ①、如何注册Chatgpt&#xff1f; ②、如何将 chatgpt接入微信&#xff1f; 然后沉寂了一个月&#xff0c;没想现在到又火了。本篇文章我…

计算机组成与体系结构 性能设计 William Stallings 第2章 性能问题

2.1 优化性能设计例如&#xff0c;当前需要微处理器强大功能的桌面应用程序包括&#xff1a;图像处理、三维渲染、语音识别、视频会议、多媒体创作、文件的声音和视频注释、仿真建模从计算机组成与体系结构的角度来看&#xff0c;一方面&#xff0c;现代计算机的基本组成与50多…

强大的ChatGpt为企业营销推广提供了全方位的加持

chatgpt&#xff0c;一个火出圈的“聊天机器人”。从写作文&#xff0c;到写代码&#xff0c;似乎没有什么是它干不了的。 ChatGpt在工业中的应用场景有哪些? 在工业领域&#xff0c;它可以用于提高生产效率&#xff0c;缩短生产周期&#xff0c;并帮助工人解决生产过程中的问…

优先级队列(PriorityQueue 和 Top-K问题)

一、PriorityQueue java中提供了两种优先级队列&#xff1a;PriorityQueue 和 PriorityBlockingQueue。其中 PriorityQueue 是线程不安全的&#xff0c;PriorityBolckingQueue 是线程安全的。 PriorityQueue 使用的是堆&#xff0c;且默认情况下是小堆——每次获取到的元素都是…

LeetCode刷题------字符串

LeetCode&#xff1a;344.反转字符串 定义两个指针&#xff08;也可以说是索引下标&#xff09;&#xff0c;一个从字符串前面&#xff0c;一个从字符串后面&#xff0c;两个指针同时向中间移动&#xff0c;并交换元素。 var reverseString function(s) {let l -1, r s.len…

Windows下LuaBridge2.8的环境配置及简单应用

Windows下LuaBridge2.8的环境配置及简单应用 LuaBridge2.8下载链接&#xff1a; https://github.com/vinniefalco/LuaBridge/tags 关于Lua的环境配置可参考以下链接&#xff08;这里不做简述&#xff09;&#xff1a; https://ufgnix0802.blog.csdn.net/article/details/125341…