【C++11/线程相关】thread类编写多线程、mutex互斥锁和lock_guard、atomic原子类型

目录

  • 通过thread类编写C++多线程程序
  • 线程间互斥——mutex互斥锁和lock_guard
    • mutex互斥锁
    • lock_guard
  • 线程间通信
    • C++11实现生产者与消费者模型
  • 基于CAS操作的atomic原子类型

橙色

通过thread类编写C++多线程程序

为什么结果没有子线程中所打印的字符串呢?因为通过detach进行了线程分离,即主线程不会等待子线程结束再继续往下运行,而是直接运行到底,所以主线程运行完直接结束了。而子线程是睡眠了两秒后才打印语句,此时主线程已经结束,也就看不到该语句了

#include<stdio.h>
#include<iostream>
#include<thread>
using namespace std;
/*
线程内容:
一.怎么创建启动一个线程
std::thread定义一个线程对象,传入线程所需要的线程函数和参数,线程自动开启

t.join():等待t线程结束,当前线程继续往下运行
t.detach():把t线程设置为分离线程,主线程结束,整个进程结束,所有子线程都自动结束了
*/
void threadHandele1(int time)
{
    //让子线程睡眠2秒
    std::this_thread::sleep_for(std::chrono::seconds(time));
    cout << "hello thread1!" << endl;
}

int main(){
    //创建了一个线程对象,传入一个线程函数,新线程就开始运行了
    thread t1(threadHandele1,2);
    //主线程等待子线程结束,主线程继续往下运行
    //t1.join(); 
    //把子线程设置为分离线程
    t1.detach();

    cout << "main thread done!" << endl;
    return 0;
}

在这里插入图片描述

线程间互斥——mutex互斥锁和lock_guard

mutex互斥锁

很值得注意的是锁+双重判断的应用。为什么要在锁上后再加上一个if语句判断呢?因为如果不加的话,当ticketCount=1时,第一个线程拿到锁,开始执行,但此时可能还没执行到ticketCount–,所以其他子线程也进入while循环,并在mtx.lock()处阻塞,此时第一个线程卖出票了并释放锁,于是已经进入while循环的第二个线程开始拿锁执行,就容易出现卖出第0张票,卖出第-1张票的情况,所以在拿到锁后再加入一个if语句判断是十分有必要的

#include<stdio.h>
#include<iostream>
#include<thread>
#include<mutex>
#include<list>
using namespace std;
/*
C++ thread 模拟车站三个窗口卖票的程序
*/
int ticketCount = 100;//车站有100张票,由三个窗口一起卖票
std::mutex mtx;//全局的一把互斥锁 

//模拟卖票的线程函数
void sellTicket(int index)
{   
    while(ticketCount>0)//ticketCount=1  锁+双重判断
    {
        mtx.lock();
        if(ticketCount>0)
        {
            cout << "窗口:" << index << "卖出第:" << ticketCount <<"张票"<< endl;
            ticketCount--;
        }
        mtx.unlock();
        std::this_thread::sleep_for(std::chrono::milliseconds(100));   
    } 
}

int main()
{
    list<std::thread> tlist;
    for (int i = 1; i <= 3;++i)
    {
        tlist.push_back(std::thread(sellTicket, i));
    }
    for(std::thread &t:tlist)
    {
        t.join();
    }
    cout << "所有窗口卖票结束!" << endl;
    return 0;
}

lock_guard

lock_guard是一个模板类

#include<stdio.h>
#include<iostream>
#include<thread>
#include<mutex>
#include<list>
using namespace std;
/*
C++ thread 模拟车站三个窗口卖票的程序
*/
int ticketCount = 100;//车站有100张票,由三个窗口一起卖票
std::mutex mtx;//全局的一把互斥锁 

//模拟卖票的线程函数
void sellTicket(int index)
{   
    while(ticketCount>0)//ticketCount=1  锁+双重判断
    {      
        {
            //出了{}作用域就会析构自动释放mtx这把锁,保证所有线程都能释放锁
            //防止死锁问题的发生
            lock_guard<std::mutex> lock(mtx);
            if(ticketCount>0)
            {
                cout << "窗口:" << index << "卖出第:" << ticketCount <<"张票"<< endl;
                ticketCount--;
            }
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(100));      
    }  
}

int main()
{
    list<std::thread> tlist;
    for (int i = 1; i <= 3;++i)
    {
        tlist.push_back(std::thread(sellTicket, i));
    }
    for(std::thread &t:tlist)
    {
        t.join();
    }

    cout << "所有窗口卖票结束!" << endl;
    return 0;
}

在这里插入图片描述

线程间通信

C++11实现生产者与消费者模型

#include <mutex>
#include <condition_variable>
#include <queue> //C++ STL里面的所有容器都不是线程安全的
#include <iostream>
#include <thread>
 
using namespace std;
 
class Queue
{
public:
    void put(int val) // 生产者
    {
        unique_lock<std::mutex> lck(mtx);
        while (!que.empty())
        {
            // que不为空,生产者应该通知消费者去消费
            // 生产者应该进入 #1等待状态 #2并把mtx互斥锁释放掉
            cv.wait(lck);
        }
        que.push(val);
        cv.notify_all(); // 通知其他所有消费者可以进行消费了
        //其他线程的得到通知就会从等待状态 ==> 阻塞状态 ==> 获取互斥锁才能继续执行
 
        cout << "生产者生产:" << val << "号物品" << endl;
    }
 
    int get() // 消费者
    {
        unique_lock<std::mutex> lck(mtx);
        while (que.empty())
        {
            // 消费者线程发现que是空的,通知生产者线程生产物品
            // #1 进入等待状态 #2把互斥锁mutex释放掉
            cv.wait(lck);
        }
        int val = que.front();
        que.pop();
        cv.notify_all(); //通知其他所有生产者可以进行生产了
        cout << "消费者消费: " << val << "号物品" << endl;
        return val;
    }
 
private:
    queue<int> que;
    mutex mtx;             // 定义互斥锁,做线程间的胡吃操作
    condition_variable cv; // 定义条件变量,做线程之间的同步通信操作
};
 
void producer(Queue *que) // 生产者线程
{
    for (int i = 1; i <= 10; ++i)
    {
        que->put(i);
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
}
 
void consumer(Queue *que) // 消费者线程
{
    for (int i = 1; i <= 10; ++i)
    {
        que->get();
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
}
 
int main()
{
    Queue que; // 两个线程共享的队列
    thread t1(producer, &que);
    thread t2(consumer, &que);
    t1.join();
    t2.join();
}

在这里插入图片描述

unique_lock与lock_guard的比较,仔细看下面的代码和注释

#include <bits/stdc++.h>
#include <mutex>
#include <condition_variable>
 
using namespace std;
 
std::mutex mtx;
std::condition_variable cv;
 
int main()
{
    /***
     * 唤醒在cv上等待的线程
     * 其他在cv上等待的线程收到通知,从等待状态 ==> 阻塞状态 ==> 获得互斥锁 ==> 线程继续向下执行
     * **/
    // cv.notify_all();
 
    // 它不仅可以用在简单的临界区代码段的互斥操作中,还能用于函数调用过程中
    unique_lock<std::mutex> lck(mtx);
    cv.wait(lck); // #两个作用:1使线程进入等待状态 2 lck.unlock()可以把mutex释放掉
 
    // 不可以用在函数参数传递或者返回过程中,只能用在简单的临界区代码段的互斥操作中
    lock_guard<std::mutex> guard(mtx);
 
    // mtx.lock();
    // mtx.unlock();
 
    return 0;
}

基于CAS操作的atomic原子类型

如果mycount不是原子类型的话,最后的值很可能就不会是1000了

#include <iostream>
#include <thread>
#include<atomic>
#include<list>
using namespace std;

atomic_bool isReady;
atomic_int mycount;

void task()
{
    while(!isReady)
    {
        this_thread::yield();//线程出让当前的cpu时间片,等待下一次调度
    }
    for (int i = 0; i < 100;i++)
    {
        mycount++;
    }
}

int main()
{
    list<std::thread> tlist;
    isReady=false;
    mycount = 0;
    for (int i = 0; i < 10;i++)
    {
        tlist.push_back(std::thread(task));
    }
    this_thread::sleep_for(chrono::seconds(3));
    isReady = true;
    
    for(thread &t:tlist)
    {
        t.join();
    }
    cout << "mycount:" << mycount << endl;
}

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

STM32串口通信初探:使用HAL库实现基本功能

在本文中&#xff0c;我们将探索如何使用STM32的HAL库来实现串口通信的基本功能。串口通信是一种常见的外设通信方式&#xff0c;用于在微控制器和其他外部设备之间进行数据传输。在STM32系列微控制器中&#xff0c;HAL库提供了简单且灵活的方法来实现串口通信。我们将重点讨论…

【模电】直流通路与交流通路

直流通路与交流通路 通常&#xff0c;在放大电路中&#xff0c;直流电源的作用和交流信号的作用总是共存的&#xff0c;即静态电流、电压和动态电流、电压总是共存的。但是由于电容、电感等电抗元件的存在&#xff0c;直流量所流经的通路与交流信号所流经的通路不完全相同。因此…

MySQL的多表查询

多表关系 一对多(多对一)-> 多对多-> 一对一-> 概述 概述 多表查询分类 内连接 代码演示--> -- 内连接演示 -- 1.查询每一个员工的姓名&#xff0c;及关联的部门的名称(隐式内连接实现) select emp.name, dept.name from emp,dept where emp.dept_id dept.id; …

STM32F407-14.3.11-01互补输出和死区插入

互补输出和死区插入 高级控制定时器&#xff08;TIM1 和 TIM8&#xff09;可以输出两路互补信号&#xff0c;并管理输出的关断与接通瞬间。 这段时间通常称为死区&#xff0c;用户必须根据与输出相连接的器件及其特性&#xff08;电平转换器的固有延迟、开关器件产生的延迟...&…

java项目日常运维需要的文档资料

一、前言 java项目开发完成&#xff0c;部署上线&#xff0c;进入项目运维阶段&#xff0c;日常工作需要准备哪些资料和文档?当项目上线后&#xff0c;运行一段时间&#xff0c;或多或少会遇到一些运维上的问题&#xff0c;比如服务器磁盘饱满&#xff0c;服务器CPU&#xff0…

根文件系统初步测试

一. 简介 上一篇文章学习了向所编译生成的根文件系统中加入 lib库文件。文章地址如下&#xff1a; 根文件系统lib库添加与初步测试-CSDN博客 本文继上一篇文章的学习&#xff0c;本文对之前制作的根文件系统进行一次初步测试。 二. 根文件系统初步测试 为了方便测试&#…

如何使用 Docker 安装 Node-RED

安装 Node-RED 使用 Docker 是一种简便的方式&#xff0c;以下是基本的步骤&#xff1a; 安装 Docker&#xff1a; 确保已在系统上安装 Docker。可从 Docker 官方网站 或 Windows Docker 安装教程 获取安装指南。 拉取运行 Node-RED 镜像&#xff1a; 打开终端或命令行界面&am…

企业架构LB-服务器的负载均衡之Haproxy实现

企业架构LB-服务器的负载均衡之HAProxy实现 学习目标和内容 1、能够通过HAProxy实现负载均衡 ###1、介绍 Introduction HAProxy, which stands for High Availability Proxy, is a popular opensource software TCP/HTTP LoadBalancer and proxying solution which can be ru…

​ 华大基因发布《2023年全球地中海贫血认知现状报告》

在地中海沿岸地区、非洲、中东、东南亚和中国南部&#xff0c;一种名为地中海贫血&#xff08;以下简称“地贫”&#xff09;的遗传性血红蛋白疾病十分高发&#xff0c;已成为严重危害公共健康和社会稳定的重大问题。近日&#xff0c;华大基因发布《2023年全球地中海贫血认知现…

pip命令详解

pip命令介绍 pip是由Ian Bicking在2008年提出的&#xff0c;他将pyinstall重命名为pip。名称pip是首字母缩写词&#xff0c;全称为“Package Installer for Python”。自Python3的3.4版本以及Python2的2.7.9版本开始&#xff0c;pip被直接包括在Python的安装包内&#xff0c;成…

二叉树(判断是否为平衡二叉树)

题目&#xff08;力扣&#xff09;&#xff1a; 观察题目&#xff0c;发现最重要的条件就是&#xff0c;两颗子树的高度差的绝对值不超过1&#xff0c;我们就可以用递归将所有左子树和右子树都遍历一个&#xff0c;求出他们的高度差&#xff0c;若差值 > 1&#xff0c;则返回…

分布式搜索引擎elasticsearch(一)

5.1 初始elasticsearch elasticsearch是一款非常强大的开源搜索引擎,可以帮助我们从海量数据中快速找到需要的内容。 elasticsearch是elastic stack的核心,负责存储、搜索、分析数据。 5.1.1正向索引 5.1.2elasticsearch采用倒排索引: 文档(document):每条数据就是一个…

ONLYOFFICE 协作空间 2.0 现已发布:新增公共房间、插件、重新分配数据、RTL 界面等功能

更新后的 ONLYOFFICE 协作空间新增诸多实用功能&#xff0c;全平台实现多项优化功能。请继续阅读&#xff0c;了解所有更新。 ONLYOFFICE 协作空间是什么 ONLYOFFICE 协作空间是一款开源效率平台&#xff0c;让您与同事、团队成员、客户、合作伙伴、承包商、赞助商和其他第三方…

PyQt6 QListWidget列表控件

​锋哥原创的PyQt6视频教程&#xff1a; 2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~共计35条视频&#xff0c;包括&#xff1a;2024版 PyQt6 Python桌面开发 视频教程(无废话…

主题色变量和var实现多套主题换肤

文章目录 一、前言1.1、[VueElementUI实现多套主题换肤](https://blog.csdn.net/u012804440/article/details/133975511)1.2、[VueElementUI实现在线动态换肤](https://blog.csdn.net/u012804440/article/details/133975570) 二、实现2.1、多主题色定义2.2、根节点属性修改2.2.…

【未解决】huggingface模型文件下载地址为什么会变?

问题描述 上次我们已经分析了huggingface加载模型时候的文件目录应该是怎么样的&#xff1f;&#xff08;感兴趣的可以主页搜索“【经验分享】huggingface模型加载过程下载到cache文件目录具体是怎么组织的&#xff1f;以及都会有什么文件目录&#xff0c;每个文件目录是什么&a…

2022年全国大学生数据分析大赛医药电商销售数据分析求解全过程论文及程序

2022年全国大学生数据分析大赛 医药电商销售数据分析 原题再现&#xff1a; 问题背景   20 世纪 90 年代是电子数据交换时代&#xff0c;中国电子商务开始起步并初见雏形&#xff0c;随后 Web 技术爆炸式成长使电子商务处于蓬勃发展阶段&#xff0c;目前互联网信息碎片化以…

WordPress免费插件大全清单【2023最新】

WordPress已经成为全球范围内最受欢迎的网站建设平台之一。要让您的WordPress网站更具功能性、效率性&#xff0c;并提供卓越的用户体验&#xff0c;插件的选择与使用变得至关重要。 WordPress插件的作用 我们先理解一下插件在WordPress生态系统中的作用。插件是一种能够为Wo…

将rtsp视频流发送到AWS Kinesis Video Streams的方案——使用Gstreamer(C++) Command Line

大纲 1 创建Kinesis Video Streams1.1 创建视频流1.2 记录Creation Time 2 创建策略2.1 赋予权限2.2 限制资源2.3 Json格式描述&#xff08;或上面手工设置&#xff09;2.4 注意事项 3 创建IAM用户3.1 生成密钥对3.2 附加策略3.3 记录访问密钥对 4 编译C 创建者库5 发送6 检查参…

openGauss训练营培训课程第1课时

课时1:openGauss全景介绍 1、介绍 openGauss 全景 1.1.openGauss总体架构介绍 本章节主要介绍了openGauss发展的历史&#xff0c;现状以及未来。对当前的DataPod和DataKit 2种openGauss当前主推的场景化产品进行了介绍。同时对openGauss的整个逻辑模块的视图进行了讲解。 …