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

news2025/2/25 12:43:07

目录

  • 通过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;
}

在这里插入图片描述

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

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

相关文章

语音合成与配音工具(视频配音、微课配音、有声读物、产品营销)

在数字时代&#xff0c;语音技术的崛起正在改变我们与技术互动的方式。现在&#xff0c;我给大家介绍一款很赞的工具——AI文字转语音配音神器&#xff0c;为您的文字赋予生动、自然的声音&#xff0c;全新的沉浸式体验即将改变您的创作方式&#xff01;非常适合用于视频配音、…

LeetCode | 572. 另一棵树的子树

LeetCode | 572. 另一棵树的子树 OJ链接 我们需要判断两棵二叉树是否相同&#xff0c;如果再判断的的时候不同我们就直接返回false&#xff0c;否则就返回true然后再检查左子树和右子树里面是否存在subRoot子树~~ bool isSameTree(struct TreeNode* q, struct TreeNode* p) {…

音频录制软件哪个好?帮助你找到最合适的一款

音频录制软件是日常工作、学习和创作中不可或缺的一部分。选择一个适合自己需求的录音软件对于确保音频质量和提高工作效率至关重要。可是您知道音频录制软件哪个好吗&#xff1f;本文将深入探讨两种常见的音频录制软件&#xff0c;通过详细的步骤指南&#xff0c;帮助您了解它…

软件设计中如何画各类图之四探索类图:揭示软件系统的静态结构

目录 1 前言2 类图的符号及说明2.1 类&#xff08;Class&#xff09;2.2 属性&#xff08;Attributes&#xff09;2.3 方法&#xff08;Methods&#xff09;2.4 关系&#xff08;Relationships&#xff09; 3 画类图的步骤3.1 确定系统范围3.2 识别类3.3 建立类之间的关系3.4 细…

oops-framework框架 之 界面管理(三)

引擎&#xff1a; CocosCreator 3.8.0 环境&#xff1a; Mac Gitee: oops-game-kit 注&#xff1a; 作者dgflash的oops-framework框架QQ群&#xff1a; 628575875 回顾 在上文中主要通过oops-game-kit大家了一个新的模版项目&#xff0c; 主要注意项是resources目录下的两个文…

leetcode 201 数字范围按位与

leetcode 201 题目题解代码 题目 给你两个整数 left 和 right &#xff0c;表示区间 [left, right] &#xff0c;返回此区间内所有数字 按位与 的结果&#xff08;包含 left、right 端点&#xff09;。 具体示例如下&#xff1a; 题解 本题是一个在思维上的方法&#xff0c;不…

HarmonyOS4.0系列——03、声明式UI、链式编程、事件方法、以及自定义组件简单案例

HarmonyOS4.0系列——03、声明式UI、链式编程、事件方法、以及自定义组件简单案例 声明式 UI ArkTS以声明方式组合和扩展组件来描述应用程序的UI&#xff0c;同时还提供了基本的属性、事件和子组件配置方法&#xff0c;帮助开发者实现应用交互逻辑。 如果组件的接口定义没有包…

Git:分布式版本控制系统的崛起与演变

简介 Git是一个开源的分布式版本控制系统&#xff0c;旨在有效、高速地处理从很小到非常大的项目版本管理。它是由Linus Torvalds于2005年创建的&#xff0c;最初是为了服务于Linux内核开发的版本控制需求。Git通过强大的分支功能、高效的缓存机制以及可扩展的架构设计&#xf…

实验六 单脉冲触发中断实验(汇编与微机原理)

实验目的&#xff1a; 掌握可编程中断控制器8259一般的使用方法。 掌握8259初始化的编程方法及中断服务程序的编写方法&#xff0c;中断程序的调试方法。 实验内容&#xff1a; 用单脉冲按钮的正脉冲输出作为中断控制器8259的中断源产生中断请求&#xff0c;在中断服务程序…

启动微服务idea控制台配置及样式

启动微服务idea控制台配置及样式 1. view —> tool windows —> services 2.控制台样式&#xff0c;下载插件 Grep Console &#xff0c;可在设置中设置颜色

element 弹窗在弹出后鼠标还可以点击页面其他元素

文章目录 需求分析需求 如下图所示,在点击弹出弹框后,支持 鼠标可点击弹框外的其他地方可拖拽弹框弹出弹出后不可有遮挡弹出样式可自定义 分析 官网:https://vxetable.cn/v4/#/table/start/install 安装 vxe-table 引入import {App, createApp }

全球大模型发展整体态势,暗流涌动下的机遇

原创 | 文 BFT机器人 (一)大模推动能“涌现”&#xff0c;打开AI术发展上限 人工智能大模型&#xff0c;是指通过在海量数据上依托强大算力资源进行训练后能完成大量不同下游任务的模型。 在技术层面上&#xff0c;大模型的实现采用“预训练指令微调人类反馈的强化学习”的训练…

3款技术宅下载神器you-get,DownKyi,Hitomi-Downloader

今天在B站看到一个无水印素材视频&#xff0c;就想着下载下来。原来我一直用硕鼠的&#xff0c;但今天硕鼠官网访问不了。python小工具比较多&#xff0c;搜索一下发现几款不错的下载小工具&#xff0c;推荐给大家。 一、准备工作 很多时候我们要做视频处理&#xff0c;或者视…

记录 | Mac微信双开

目的&#xff1a;在 mac 上微信双开 (1) 先打开并登录第一个微信&#xff1b; 2&#xff09;访达 -> 应用程序 -> 微信&#xff08;双指同时摁&#xff09;-> 显示包内容&#xff1b; 3&#xff09;依次打开以下⽂件夹 Contents -> MacOS -> 双击 WeChat 即可…

Redis 安装部署

文章目录 1、前言2、安装部署2.1、单机模式2.1.1、通过 yum 安装&#xff08;不推荐&#xff0c;版本老旧&#xff09;2.1.1、通过源码编译安装&#xff08;推荐&#xff09; 2.2、主从模式2.3、哨兵模式2.4、集群模式2.5、其他命令2.6、其他操作系统 3、使用3.1、Java 代码 —…

开源数据大屏系统介绍

睿思BI数据大屏系统现已开源&#xff0c;通过拖拽配置的方式构建大屏&#xff0c;支持零代码开发。并且包含大量大屏模版&#xff0c;方便用户快速创建大屏应用。 系统主要包括数据准备、大屏设计、权限管理3个部分内容。 1.数据准备 1.1 创建数据源&#xff1a;定义BI系统链…

基于HTML 实现的示波器-含完整源码

完整资料下载连接 基于HTML 实现的示波器-含jshtmlcss完整源码 改源码是在桌面 PC 上设计的&#xff0c;分辨率为 1920 x 1080 像素&#xff0c;但宽高像素比为 1.4 到 1.6 的任何分辨率都将产生良好的图像。它适用于以下浏览器的最新版本&#xff1a;Internet Explorer、Edge、…

如何利用企业软件著作权查询API提升知识产权管理效率

引言 在当今数字化时代&#xff0c;企业的知识产权管理变得愈发重要。其中&#xff0c;软件著作权作为企业重要的知识产权之一&#xff0c;其保护和管理对于企业的创新和竞争力至关重要。为了更高效地进行软件著作权管理&#xff0c;许多企业开始采用先进的技术手段&#xff0…

初识谷歌chrome插件

谷歌插件想必各位都用过&#xff0c;使用广泛的vue-tools想必大家都不陌生吧&#xff0c;这就是谷歌插件。与其说是谷歌插件&#xff0c;倒不如说是浏览器插件&#xff0c;只是谷歌浏览器用的比较普遍罢了。所以这里就用谷歌插件代称吧。 1.何为插件 先来看下比较官方的定义&a…

Spring 向页面传值以及接受页面传过来的参数的方式

一、从页面接收参数 Spring MVC接收请求提交的参数值的几种方法&#xff1a; 使用HttpServletRequest获取。 RequestMapping("/login.do") public String login(HttpServletRequest request){ String name request.getParameter("name") String pa…