c++互斥锁,竞争状态与临界区

竞争状态与临界区

  • 1,基本互斥锁
  • 2,try_lock
  • 3,互斥锁存在的坑—线程抢占不到资源
  • 4,超时锁
  • 5,递归锁(在一个线程内可以多次lock的锁)recursive_mutex和recursive_timed_mutex用于业务组合
  • 6,利用栈特性自动释放锁RALL
    • 6.1手动实现简易RALL
    • 6.2 c++11支持的RALL管理互斥资源 lock_guard
    • 6.3 c++11 unique_lock c++11

1,基本互斥锁

//互斥锁使用
#include<mutex>
static  mutex  mux;
mux.lock();
mux.unlock();

案例

// An highlighted block
#include<iostream>
#include<thread>
#include<string>
#include<windows.h>
#include<chrono>
#include<mutex>
using namespace std;
static  mutex  mux;void  TestThread1()
{mux.lock();cout << "=====================================" << endl;cout << "test 001" << endl;cout << "test 002" << endl;cout << "test 003" << endl;cout << "=====================================" << endl;mux.unlock();std::this_thread::sleep_for(std::chrono::milliseconds(50));
}void deal1()
{thread  th(TestThread1);th.join();
}int  main()
{deal1();
}

运行结果
在这里插入图片描述
互斥锁的作用在于可以尽可能的避免数据竞争,在lockunlock之间的区域被称之为临界区,要注意一点的是,临界区要尽量小,锁要尽早的申请且尽早的释放

2,try_lock

if (!mux.try_lock()){}
//尝试去锁,成功返回true,失败返回false

案例:

#include<windows.h>
#include<chrono>
#include<mutex>
using namespace std;
static  mutex  mux;void  TestThread1()
{for (;;){if (!mux.try_lock()){cout << "." << "flush" << endl;;this_thread::sleep_for(std::chrono::milliseconds(50));continue;}//mux.lock();cout << "=====================================" << endl;cout << "test 001" << endl;cout << "test 002" << endl;cout << "test 003" << endl;cout << "=====================================" << endl;mux.unlock();std::this_thread::sleep_for(std::chrono::milliseconds(50));}}void  deal1()
{for (int i = 0;i<10;i++){thread  th1(TestThread1);th1.detach();}getchar();
}
int  main()
{deal1();
}

注意,try_lock有性能开销。

3,互斥锁存在的坑—线程抢占不到资源

在2节中,unlock之后选择让线程睡眠50毫秒,为什么要要sleep 50毫秒呢???这就涉及到多线程并发时因多线程竞争cpu资源而容易出现的坑。

线程有这样几种状态,
1,初始化(Init): 表示该线程正在创建。
2,就绪(Ready):表示该线程在就绪列表中,等待CPU调度。
3,运行(Running): 表示该线程正在运行。
4,阻塞(Blocked):该线程被阻塞挂起,包括,锁,事件,信号量阻塞。此状态不占用cpu资源
5,退出:该线程结束,等待父线程回收资源。

在这里插入图片描述
一个线程在创建,就绪之后,肯直接进入阻塞状态,也可能先进入运行状态,后进入阻塞状态。当由运行状态进入阻塞状态之后,线程在阻塞一段时间之后可能因为等待互斥锁或条件变量,重新进入就绪态,等待cpu调度之后,才再次进入运行状态。

实际上,多线程并发运行,在某一个时间段之内其实只有一个线程在占用cpu资源,当某一个线程调用unlock之后,所有线程会再次去争夺cpu资源,此时会出现一个神奇的现象,那就是,某一个线程在unlock之后,cpu资源被释放,然后多个线程再次竞争cpu资源的时候,上一个占用cpu资源的线程再次获取到了cpu资源,导致某一个线程长期能抢占到cpu资源而其他线程就无法顺利运行下去,这种情况是我们在实际的项目当中不想遇到的,于是乎,在调用unlock之后,让现场 sleep 一段事件,这样就可以让其他线程争夺cpu资源,就不会出现某一个线程长期占用cpu资源的场景了。

4,超时锁

static timed_mutex  tmux;
if (!!tmux.try_lock_for(chrono::milliseconds(500))){}
//尝试获取锁,当五百毫秒内没有获取到锁资源,就会返回false

案例:

#include<iostream>
#include<thread>
#include<string>
#include<windows.h>
#include<chrono>
#include<mutex>
using namespace std;
static timed_mutex  tmux;void  ThewadMain(int i)
{for (;;){if (!tmux.try_lock_for(chrono::milliseconds(500))){cout << i << "  lock failed " << endl;continue;}else{cout << i << " [in]" << endl;tmux.unlock();std::this_thread::sleep_for(chrono::microseconds(2000));}}
}void  deal1()
{for (int i = 0; i < 3; i++){thread th(ThewadMain,i);th.detach();}
}int  main()
{deal1();getchar();
}

运行结果:
在这里插入图片描述

5,递归锁(在一个线程内可以多次lock的锁)recursive_mutex和recursive_timed_mutex用于业务组合

在我们开发某个项目的时候,随着时间的推移,项目代码越来越多,接口越来越多,会出现大量的接口层层调用的情况,肯会出现的一个情况是,某个外部接口已经对某个锁进行了 lock 但是调用的两一个接口也对某一个锁进行了lock ,同一个锁在同一个线程内连续调用两次lock会发生什么现象呢?

// An highlighted block
#include<iostream>
#include<thread>
#include<string>
#include<windows.h>
#include<chrono>
#include<mutex>
using namespace std;
static mutex  mux;void Task1()
{mux.lock();cout << "task1" << endl;mux.unlock();
}void  Task2()
{mux.lock();cout << "task2" << endl;mux.unlock();
}void  ThewadMain(int i)
{mux.lock();Task1();Task2();mux.unlock();
}void  deal1()
{thread th1(ThewadMain,1);
}int  main()
{deal1();getchar();
}

在这里插入图片描述
我们发现,程序直接崩溃无法运行。
为了解决这个问题我们引入了递归锁,所谓的递归锁就是可以在一个线程中锁多次。

static recursive_mutex  rmux;
#include<iostream>
#include<thread>
#include<string>
#include<windows.h>
#include<chrono>
#include<mutex>using namespace std;
static recursive_mutex  rmux;void Task1()
{rmux.lock();cout << "task1" << endl;rmux.unlock();
}void  Task2()
{rmux.lock();cout << "task2" << endl;rmux.unlock();
}void  ThewadMain(int i)
{rmux.lock();Task1();Task2();rmux.unlock();
}void  deal1()
{thread th1(ThewadMain,1);th1.detach();
}int  main()
{deal1();getchar();
}

在这里插入图片描述
递归锁可以多次lock,但是也要相应的进行多次unlock,比如我lock两次,那么我也要相应的unlock两次。

6,利用栈特性自动释放锁RALL

RALL c++之父提出,使用局部对象来管理资源的技术成为资源获取即初始化,它的生命周期是由操作系统来管理的,无需人工介入;资源的销毁容易忘记,造成死锁或内存泄露。

简单来说就是,我们自己管理锁需要手动 lock 或者 手动 unlock ,但是随着项目周期的拉长我们很可能忘记 unlock 造成死锁,此时我们用一种方法可以不用手动实现 lock unlock 即可实现自动释放,尽可能的避免死锁或内存泄露。

6.1手动实现简易RALL

#include<iostream>
#include<thread>
#include<string>
#include<windows.h>
#include<chrono>
#include<mutex>using namespace std;
static mutex  rmux;class  XMutex
{
public:XMutex(mutex& mux):mux_(mux){mux_.lock();}~XMutex(){mux_.unlock();}
private:mutex& mux_;
};void  ThreadMain(int i)
{XMutex  lock(rmux);cout << i<<" in thread" << endl;
}void  deal1()
{for (int i = 0; i < 10; i++){thread th(ThreadMain,i);th.detach();}
}int  main()
{deal1();getchar();
}

在这里插入图片描述

6.2 c++11支持的RALL管理互斥资源 lock_guard

// A code block
static mutex  rmux;
lock_guard<mutex> lock(rmux);
#include<iostream>
#include<thread>
#include<string>
#include<windows.h>
#include<chrono>
#include<mutex>
using namespace std;
static mutex  rmux;void  ThreadMain(int i)
{{lock_guard<mutex> lock(rmux);cout << i << " in thread" << endl;}std::this_thread::sleep_for(chrono::microseconds(200));}void  deal1()
{for (int i = 0; i < 10; i++){thread th(ThreadMain,i);th.detach();}
}int  main()
{deal1();getchar();
}

如上述代码所示,当出了大括号的范围,自动调用析构函数,会自动进行unlock,。

6.3 c++11 unique_lock c++11

上一个小结的 lock_guard 是一个最基本的简单的 RALL 锁,但实际的项目开发过程会出现更复杂的业务需求,比如会存在 一个锁赋值给另一个锁,一个锁移动到另一个锁,或者可能会出现临时手动解锁加锁,或者其他更复杂的需求,此时我们就会使用 unique_lock 。

// A code block
unique_lock  c++11  
支持临时释放锁 unlock
支持 adopt_lock (已经拥有锁,不加锁,出栈区会释放)
支持 defer_lock (延后拥有,不加锁,出栈区不释放)
支持 try_to_lock 尝试获得互斥的所有权而不阻塞,获取失败推出栈区不会释放,通过 owns_lock() 函数判断。
// An highlighted block
#include<iostream>
#include<thread>
#include<string>
#include<windows.h>
#include<chrono>
#include<mutex>
using namespace std;
static mutex  rmux;void  ThreadMain(int i)
{{unique_lock<mutex> lock(rmux);cout << i << " in thread" << endl;}std::this_thread::sleep_for(chrono::microseconds(200));}void  deal1()
{for (int i = 0; i < 10; i++){thread th(ThreadMain,i);th.detach();}
}int  main()
{deal1();getchar();
}

支持临时释放锁

// An highlighted block
void  ThreadMain(int i)
{{unique_lock<mutex> lock(rmux);lock.unlock();cout << i << " in thread" << endl;lock.lock();}std::this_thread::sleep_for(chrono::microseconds(200));
}

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

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

相关文章

实战项目:基于控制台与数据库的图书管理系统开发指南

一、项目概述与设计思路 1.1 为什么选择图书管理系统 图书管理系统是学习编程的经典项目&#xff0c;它涵盖了&#xff1a; 控制台交互&#xff1a;学习用户输入输出处理 数据库操作&#xff1a;掌握CRUD核心功能 业务逻辑&#xff1a;理解实际应用场景 系统架构&#xff…

人工智能——层次聚类算法

目录 摘要 18 层次聚类 18.1 本章工作任务 18.2 本章技能目标 18.3 本章简介 18.4 编程实战 18.5 本章总结 18.6 本章作业 本章已完结&#xff01;&#xff01;&#xff01; 摘要 本章实现的工作是&#xff1a;首先导入20名学生的3科成绩&#xff0c;然后根据优先聚…

Linux中安装mysql8,转载及注意事项

一、先前往官网下载mysql8 下载地址&#xff1a; https://dev.mysql.com/downloads/选择Linux 二、删除Linux中的mysql&#xff08;如果有的话&#xff09;&#xff0c;上传安装包 1、先查看mysql是否存在&#xff0c;命令如下&#xff1a; rpm -qa|grep -i mysql如果使用这…

《算法导论(第4版)》阅读笔记:p4-p5

《算法导论(第4版)》学习第 3 天&#xff0c;p4-p5 总结&#xff0c;总计 2 页。 一、技术总结 1.instance Thus, given the input sequence h31; 41; 59; 26; 41; 58i, a correct sorting algorithm returns as output the sequence h26; 31; 41; 41; 58; 59i. Such an inp…

第十四篇:系统分析师第三遍——15章

目录 一、目标二、计划三、完成情况四、意外之喜(最少2点)1.计划内的明确认知和思想的提升标志2.计划外的具体事情提升内容和标志 五、总结六、后面准备怎么做&#xff1f; 一、目标 通过参加考试&#xff0c;训练学习能力&#xff0c;而非单纯以拿证为目的。 1.在复习过程中&…

Easy云盘总结篇-登录注册

**说在前面&#xff1a;该项目是跟着B站一位大佬写的&#xff0c;不分享源码&#xff0c;支持项目付费 ** 获取图形验证码 可以看到这里有2两种图形验证码&#xff0c;分为&#xff1a; type0&#xff1a;如上图下面那个&#xff0c;是完成操作后要进行注册的验证码 type1: 如…

【前端知识】Vue3状态组件Pinia详细介绍

Vue3状态组件Pinia详细介绍 关联知识 Pinia 组件介绍、核心原理及使用方式 Pinia 组件介绍 Pinia 是 Vue.js 的官方状态管理库&#xff0c;专为 Vue 3 设计&#xff0c;提供简洁的 API 和强大的 TypeScript 支持。其核心组件包括&#xff1a; • Store&#xff1a;状态存储容器…

mysql 云服务远程linux创建数据库

1. 本地使用已创建好的用户创建数据库出现问题 提示access deniey finalshell远程创建新用户 :~# mysql -u root -pR***34 > CREATE DATABASE r***e; > CREATE USER r**ue% IDENTIFIED BY Ry****34; > GRANT ALL PRIVILEGES ON ry_vue.* TO r***e%; > FLUSH PRI…

【“星瑞” O6 评测】 — CPU llama.cpp不同优化速度对比

前言 随着大模型应用场景的不断拓展&#xff0c;arm cpu 凭借其独特优势在大模型推理领域的重要性日益凸显。它在性能、功耗、架构适配等多方面发挥关键作用&#xff0c;推动大模型在不同场景落地 1. Kleidi AI 简介 Arm Kleidi 成为解决这些挑战的理想方案&#xff0c;它能…

wireshark抓包也能被篡改?

wireshark本身并不能修改数据包&#xff0c;但是tcprewrite 可以修改数据包&#xff0c;然后通过tcpreplay 进行重放&#xff0c;这个时候wireshark抓的包&#xff0c;就是被篡改后的pcap包了。 ailx10 网络安全优秀回答者 互联网行业 安全攻防员 去咨询 步骤一&#xff1a…

使用PyTorch进行热狗图像分类模型微调

本教程将演示如何使用PyTorch框架对预训练模型进行微调&#xff0c;实现热狗与非热狗图像的分类任务。我们将从数据准备开始&#xff0c;逐步完成数据加载、可视化等关键步骤。 1. 环境配置与库导入 %matplotlib inline import os import torch from torch import nn from d2l…

内容中台与企业内容管理核心差异剖析

功能定位与架构设计差异 在企业数字化进程中&#xff0c;内容中台与企业内容管理&#xff08;ECM&#xff09;的核心差异首先体现在功能定位层面。传统ECM系统以文档存储、版本控制及权限管理为核心&#xff0c;主要服务于企业内部知识库的静态管理需求&#xff0c;例如通过Ba…

使用PyMongo连接MongoDB的基本操作

MongoDB是由C语言编写的非关系型数据库&#xff0c;是一个基于分布式文件存储的开源数据库系统&#xff0c;其内容存储形式类似JSON对象&#xff0c;它的字段值可以包含其他文档、数组及文档数组。在这一节中&#xff0c;我们就来回顾Python 3下MongoDB的存储操作。 常用命令:…

第 12 届蓝桥杯 C++ 青少组中 / 高级组省赛 2021 年真题

一、选择题 第 1 题 题目&#xff1a;下列符号中哪个在 C 中表示行注释 ( )。 A. ! B. # C. ] D. // 正确答案&#xff1a;D 答案解析&#xff1a; 在 C 中&#xff0c;//用于单行注释&#xff08;行注释&#xff09;&#xff0c;从//开始到行末的内容会被编译器忽略。选项 A…

【python】【UV】一篇文章学完新一代 Python 环境与包管理器使用指南

&#x1f40d; UV&#xff1a;新一代 Python 环境与包管理器使用指南 一、UV 是什么&#xff1f; UV 是由 Astral 团队开发的高性能 Python 环境管理器&#xff0c;旨在统一替代 pyenv、pip、venv、pip-tools、pipenv 等工具。 1.1 UV 的主要功能 &#x1f680; 极速包安装&…

前端性能优化2:结合HTTPS与最佳实践,全面优化你的网站性能

点亮极速体验&#xff1a;结合HTTPS与最佳实践&#xff0c;为你详解网站性能优化的道与术 在如今这个信息爆炸、用户耐心极其有限的数字时代&#xff0c;网站的性能早已不是一个可选项&#xff0c;而是关乎生存和发展的核心竞争力。一个迟缓的网站&#xff0c;无异于在数字世界…

JavaWeb:vueaxios

一、简介 什么是vue? 快速入门 <!-- 3.准备视图元素 --><div id"app"><!-- 6.数据渲染 --><h1>{{ msg }}</h1></div><script type"module">// 1.引入vueimport { createApp, ref } from https://unpkg.com/vu…

Tauri联合Vue开发中Vuex与Pinia关系及前景分析

在 TauriVue 的开发场景中&#xff0c;Vuex 和 Pinia 是两种不同的状态管理工具&#xff0c;它们的关系和前景可以从以下角度分析&#xff1a; 一、Vuex 与 Pinia 的关系 继承与发展 Pinia 最初是作为 Vuex 5 的提案设计的&#xff0c;其目标是简化 Vuex 的复杂性并更好地适配 …

Linux中的时间同步

一、时间同步服务扩展总结 1. 时间同步的重要性 多主机协作需求&#xff1a;在分布式系统、集群、微服务架构中&#xff0c;时间一致性是日志排序、事务顺序、数据一致性的基础。 安全协议依赖&#xff1a;TLS/SSL证书、Kerberos认证等依赖时间有效性&#xff0c;时间偏差可能…

【算法基础】三指针排序算法 - JAVA

一、基础概念 1.1 什么是三指针排序 三指针排序是一种特殊的分区排序算法&#xff0c;通过使用三个指针同时操作数组&#xff0c;将元素按照特定规则进行分类和排序。这种算法在处理包含有限种类值的数组时表现出色&#xff0c;最经典的应用是荷兰国旗问题&#xff08;Dutch …