【c++】c++线程库的基本使用

news2024/11/25 10:35:14
在这里插入图片描述

💻文章目录

  • 📄前言
  • C++线程库
    • 创建线程
    • 互斥量
      • mutex
      • lock_guard
      • unique_lock
    • 同步机制
      • condition_variable
      • C++20 信号量 的工作原理。
  • 📓总结


📄前言

在C++线程库推出之前,如果要实现跨平台多线程,那么我们就得需要直到每个平台的线程API的知识,这无疑对每个程序员都是不小的挑战,毕竟大部分人都不愿意去一一学习这些接口,而线程库的推出,封装了线程底层的实现,不仅提高了代码的可移植性,还减少了C++的学习压力。

C++线程库

创建线程

thread是c++11引进的线程管理机制,主要用于封装系统底层的线程API, 例如windows的 CreateThread 和linux的pthread_create,从而提高代码的可移植性。

  • thread的函数原型与参数
template <class Function, class... Args>
explicit thread (Function&& f, Args&&... args);
// 创建一个线程,可用lamda、函数指针+参数等形式

id get_id() const noexcept;	

bool joinable() const noexcept;
  • 成员函数
    • get_id:返回线程id
    • joinable:检查一个线程是否可以被回收。
    • join:回收线程的资源,防止僵死线程,如果线程没有执行完,则阻塞等待。
    • detach:与主线程分离,线程执行完毕后自动回收资源。

函数使用示例:

void func(std::string str)
{
	std::cout << std::this_thread::get_id() << std::endl;
    for(int i = 0; i < 10; i++)
    {
        std::cout << str << ":" << i << std::endl;
    }
}

int main() {
    std::thread t1(func, "thread 1");
    std::thread t2([]() { func("thread 2"); });

    t1.join();
    t2.join();

    return 0;
}

互斥量

mutex

mutex 是c++线程库最基础的互斥机制,用于保护临界资源不被多个线程同时访问,mutex 只提供了基础的锁和解锁接口。

class mutex {
public:
	lock();
	unlock();
	try_lock();	
}
  • 成员函数:
    • lock:锁定互斥量,只允许一个线程访问,
    • unlock:解锁互斥量,
    • try_lock:尝试锁定互斥量,如果互斥量被锁定则不阻塞,一直检查锁是否解锁(返回0),直到锁被解锁(返回1)。

lock_guard

lock_guard 与互斥量一起使用,提供一种RAII风格的管理机制(析构时自动调用unlock),避免忘记解锁而导致死锁的情况。

template <class Mutex> class lock_guard;

unique_lock

unique_lock 的基础用法与lock_guard一样,但比它提供了更多的功能,如控制锁的时间、方式,对条件变量的支持,以及可被移动(不可被复制)

template <class Mutex> class unique_lock;

template <class Clock, class Duration>
  bool try_lock_until (const chrono::time_point<Clock,Duration>& abs_time);

template <class Rep, class Period>
  bool try_lock_for (const chrono::duration<Rep,Period>& rel_time);
  
// 复制构造
unique_lock (const unique_lock&) = delete;
// 移动构造
unique_lock (unique_lock&& x);
  • 常用成员函数:
    • try_lock:不阻塞地申请锁
    • try_lock_for:与chrono类一起使用,等待指定的时间后去申请锁,申请失败继续等待。
    • try_lock_until:在指定的时间到底前都能一直申请锁,时间到达但没获得锁,则继续执行其他任务。

函数使用示例:

void fun_c(std::string name, int n)
{
    std::unique_lock<std::timed_mutex> lock(t_mtx, std::defer_lock);
    if(lock.try_lock_for(std::chrono::seconds(n)))
    {
        std::cout << name << " ";
        for(int i = 0; i < 10; ++i)
        {
            std::unique_lock<std::mutex> lock(mtx);
            std::cout << counter++ << std::endl;
        }
        std::this_thread::sleep_for(std::chrono::seconds(6));
    }
    else 
        std::cout << "can't lock" << std::endl;

    std::cout << name << "end" << std::endl;
}

int main() {
    std::thread t1(fun_c, "thread 1:", 2);
    std::thread t2(fun_c, "thread 2:", 2);

    t1.join();
    t2.join();
    return 0;
}

同步机制

condition_variable

condition_variable 是对系统条件变量的包装,用于实现线程直接的等待通知机制,需要与互斥量一起使用,拥有两大功能 wait 和 notify。

class condition_variable;
  • 成员函数:
    • wait:线程在这个函数中等待,直到接受到信号通知,和unique_lock 一样,它也有wait_for 和 wait_until,需要与互斥量一起使用。线程等待时会将锁给解锁。
    • notify_once:通知一个等待着的线程,并唤醒它。
    • notify_all:通知所有在wait等待着的线程,唤醒它们。

函数使用

std::condition_variable cv;
std::mutex mtx;
bool flag = false;
int counter = 0;

void producer(std::string name)
{
    // 通过条件变量可以控制线程的执行顺序
    std::cout << name << std::endl;
    for(int i = 0; i < 10; ++i)
    {
        {
            std::unique_lock<std::mutex> lock(mtx);
            while(flag)	
                cv.wait(lock);	// wait时会自动将锁解锁
            std::cout << ++counter << std::endl;
            flag = !flag;
            cv.notify_one();
        }
		std::this_thread::sleep_for(std::chrono::seconds(1));
    }
}

void consumer(std::string name)
{
    std::cout << name << std::endl;

    for(int i = 0; i < 10; ++i)
    {
        {
            std::unique_lock<std::mutex> lock(mtx);
            while(!flag)
                cv.wait(lock);

            std::cout << ++counter << std::endl;
            flag = !flag;
            cv.notify_one();	//唤醒一个线程
        }
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
}


int main() {
    std::thread t1(producer, "thread 1");
    std::thread t2(consumer, "thread 2");

    t1.join();
    t2.join();
    return 0;
}

C++20 信号量 的工作原理。

在c++20中新增了轻量级的线程控制机制——信号量,我们可以为信号量设定最大值与初始值,当信号量达到0或者最大值时,线程会阻塞,直到其不为0或最大值。

// 头文件 <semaphore>
template< std::ptrdiff_t LeastMaxValue = /* implementation-defined */ >
class counting_semaphore;
//尚未定义ptrdiff_t 一般为int,用于定义信号量的最大值。
  • 成员函数:
    • release:释放信号量,将信号量计数加 1 ,如果计数达到最大值则阻塞等待,知道信号量被消费。
    • acquire:申请信号量,将信号量的计数减 1 ,当计数为零或为满则阻塞等待,直到其他线程释放信号量。
    • try_acquire:尝试去减少信号量的计数,如果信号量为零则返回false,并且一直申请信号量直至成功。一般用于信号量消费较快的场景。

函数使用

int counter = 0;

void producer(std::string name)
{
    
    std::cout << name << std::endl;
    for(int i = 0; i < 10; ++i)
    {
        sem.try_acquire();	//信号量也可以用来代替锁
        std::cout << ++counter << std::endl;
        sem.release();
    }
}

void consumer(std::string name)		
{
    std::cout << name << std::endl;

    for(int i = 0; i < 10; ++i)
    {
        sem.try_acquire();
        std::cout << ++counter << std::endl;
        sem.release();
    }
}


int main() {
    std::thread t1(producer, "thread 1");
    std::thread t2(consumer, "thread 2");

    t1.join();
    t2.join();
    return 0;
}

📓总结

功能类别关键特性描述
线程管理std::thread抽象系统级线程,简化线程创建和管理。
基础同步mutex, lock_guard, unique_lock提供基本的线程同步机制,如互斥锁,以及自动锁管理工具。
高级同步condition_variable使线程能在特定条件下挂起和唤醒,优化资源利用和线程协调。
C++20新特性counting_semaphore引入信号量概念,为线程提供更细粒度的控制,如限流并发操作。

📜博客主页:主页
📫我的专栏:C++
📱我的github:github

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

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

相关文章

Vue3报错:‘defineProps‘ is not defined no-undef

解决方法 在package.json中添加 "vue/setup-compiler-macros": true 记得在上面的 "node": true 后面加一个逗号 "eslintConfig": {"root": true,"env": {"node": true,"vue/setup-compiler-macros": t…

虚拟网络设备性能优化

在现代网络架构中&#xff0c;虚拟网络设备扮演着越来越重要的角色&#x1f310;&#xff0c;特别是在云计算☁️和容器化技术&#x1f4e6;广泛应用的背景下。虚拟网络设备如虚拟以太网设备&#xff08;veth&#xff09;、虚拟交换机&#xff08;vSwitch&#xff09;、和虚拟路…

Vue.js 过渡

过渡 Vue 在插入、更新或者移除 DOM 时&#xff0c;提供多种不同方式的应用过渡效果。 Vue 提供了内置的过渡封装组件&#xff0c;该组件用于包裹要实现过渡效果的组件。 语法格式&#xff1a; <transition name "nameoftransition"><div></div&…

广度优先搜索--什么是“BFS”?为什么要用队列实现?--走迷宫代码详细注释

目录 什么是“DFS”什么是“BFS”为什么要用队列&#xff1f;举例&#xff08;走迷宫&#xff09;&#xff1a; 什么是“DFS” DFS 全称是 Depth First Search&#xff0c;中文名是深度优先搜索&#xff0c;是一种用于遍历或搜索树或图的算法。 深度优先&#xff0c;就是每次…

uni-admin初始化一直提示未初始化数据库问题

uni-admin初始化&#xff0c;一直提示&#xff1a; “检测到您未初始化数据库&#xff0c;请先右键uni-admin项目根目下的 uniCloud/database 目录&#xff0c;执行初始化云数据库&#xff0c;否则左侧无法显示菜单等数据” 最后清除了localStorage&#xff0c;发现就好了。

【MySQL】数据操作语句(DML)

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前学习计网、mysql和算法 ✈️专栏&#xff1a;MySQL学习 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章对你有帮助的话 欢迎 评论&#x1f4ac…

2.Spring 核心与设计思想

文章目录 1.Spring 是什么&#xff1f;1.1 什么是容器&#xff1f;1.2 什么是 IoC&#xff1f;1.2.1 传统程序开发1.2.2 控制反转式程序开发1.2.3 对比总结规律 1.3 理解 Spring IoC1.4 DI 概念说明 2.总结2.总结 大家好&#xff0c;我是晓星航。今天为大家带来的是 Spring核心…

vue3从精通到入门18:依赖注入Provide / Inject

provide 和 inject 是一对用于实现依赖注入的 API。provide 选项允许父组件向其所有子组件提供一个依赖&#xff0c;无论组件层次结构有多深&#xff0c;只要在其后代组件中使用 inject 选项&#xff0c;就可以访问到这个依赖。 父组件 (ParentComponent.vue) <template>…

抖音评论ID提取工具|视频关键词评论批量采集软件

抖音评论ID提取工具&#xff1a;批量抓取抖音评论 抖音评论ID提取工具是一款功能强大的软件&#xff0c;可以帮助您批量抓取抖音视频下的评论信息。通过输入关键词和评论监控词&#xff0c;即可进行评论的抓取&#xff0c;并提供评论昵称、评论日期、评论内容、命中关键词以及所…

C++笔记(函数重载)

目录 引入&#xff1a; 定义&#xff1a; 易错案例&#xff1a; 引入&#xff1a; 对于实现相似功能的函数&#xff0c;在命名时&#xff0c;我们常会出现命名重复的问题。对于C语言&#xff0c;编译器遇到这种命名重复的情况&#xff0c;会进行报错。而我们的C为了更方便程…

[23年蓝桥杯H题] 合并石子

问题描述 在桌面从左至右横向摆放着 N 堆石子。每一堆石子都有着相同的颜色&#xff0c;颜 色可能是颜色 0 &#xff0c;颜色 1 或者颜色 2 中的其中一种。 现在要对石子进行合并&#xff0c;规定每次只能选择位置相邻并且颜色相同的两堆 石子进行合并。合并后新堆的相对位置保…

Python对docx文本一些操作

文本要是docx结尾 安装 Python-docx 包 读取word from docx import Document doc Document("c:/word22.docx") 获取word中的所有表格 from docx import Document doc Document("c:/word22.docx") doc.tables # 返回所有表格的list 获取表格中的总行…

2022年蓝桥杯省赛——直线

目录 题目链接&#xff1a;11.直线 - 蓝桥云课 (lanqiao.cn) 题目描述 思路 代码思路如下 代码实现 坑来喽~~ 导致这个BUG的原因&#xff01;&#xff01;&#xff01; 总结 整体的 两种b的情况对比数据 题目链接&#xff1a;11.直线 - 蓝桥云课 (lanqiao.cn) 题目描…

Linux LVM磁盘扩容

1、查看磁盘情况 df -h df -h2、查看逻辑卷 lvdisplay lvdisplay3、查看逻辑组 vgdisplay vgdisplay4、查看物理卷 pvdisplay pvdisplay5、查看磁盘 fdisk -l fdisk -l6、磁盘分区fdisk /dev/磁盘名 # 上一步查看到的新硬盘路径 fdisk /dev/vdb7、格式化磁盘mkfs -t ext4…

移除chromeDriver脚本文件

#!/bin/bash process_namechromedriver process_name2webdriver #查找并杀死进程名 PIDps -ef |grep $process_name|grep -v grep|awk {print $2}|xargs PID2ps -ef |grep $process_name2|grep -v grep|awk {print $2}|xargs save_file"/usr/local/software/ship/sadmin/…

好用的Android Studio插件管理器

1.使用阿里云的通义灵码方便快速开发 1.1下载插件File->plugin->marketplace 搜索 Tongyilingma然后安装重启登录阿里云&#xff0c;确认 1.2 使用方法 输入信息描述 比如 //写一段冒泡排序然后换行&#xff0c;输入public/private/protected方法会自动生成联想代码…

javaWeb网上零食销售系统

1 绪 论 目前&#xff0c;我国的网民数量已经达到7.31亿人&#xff0c;随着互联网购物和互联网支付的普及&#xff0c;使得人类的经济活动进入了一个崭新的时代。淘宝&#xff0c;京东等网络消费平台功能的日益完善&#xff0c;使得人们足不出户就可以得到自己想要的东西。如今…

短剧查询前端程序带 API 后台管理

搭建教程 1 上传到服务器后&#xff0c;访问下 ip/api.php 2 这样就会自动创建表 3 然后在 config.php 填写数据库相关信息&#xff0c;在 admin.php 填写账号密码信息 4 访问 ip/admin.php 进去后台 5 批量插入数据格式为 短剧名短裤链接 举例&#xff1a;你好https://q…

【建议收藏合集整理】国一大佬带你,蓝桥杯Java组拿奖基础知识整理集合,看完,3天冲蓝桥杯省一。

前文引导 此文章为系统训练&#xff0c;建议反复观看&#xff0c;将所有例题的知识点掌握&#xff0c;文章结尾将分享新手如何通过3天达到冲击蓝桥杯省一的实力。 蓝桥杯Java知识点记忆&#xff08;必须熟练记忆&#xff09; 以下内容必须掌握 &#xff08;1&#xff09;输入…