常用的锁和死锁以及避免死锁

news2025/1/12 20:47:48

走近锁的世界

  • 什么是锁
  • 常用的锁
    • 互斥量
      • 基本用法
    • 无锁 CAS
  • 死锁和避免死锁
    • 死锁
    • 避免死锁

什么是锁

  在并发编程中,为了保护多线程同时访问的共享数据,以及避免出现意向不到的结果,锁(应运而生)
 简单讲下为什么数据结果有时候会与设想不一致。那就要先了解下原子性、临界区

小黑板:

  • 原子性:
    指事务的不可分割性,一个事务的所有操作要么不间断地全部被执行,要么一个也没有执行。概括起来就是,执行了就执行完,不能被打断
  • 临界区:
    一个访问共用资源(例如:共用设备或是共用存储器)的程序片段,而这些共用资源又无法同时被多个线程访问的特性。

 以++i为例,指对变量i做+1的操作。看起来是一句话,实际上在汇编运行中会变成如下三个步骤

  1. 把i加载到寄存器
  2. 对i进行+1操作
  3. 把计算结果放回内存

 在不上锁的情况下,这3个步骤在并发编程中是会被打断的,也就是创建了10个线程,每个线程跑1000次的话最后结果不一定为10000。故为了保证一体,我们将把它锁死。

常用的锁

名称说明使用场景
互斥量 mutex获取不到锁的时候就休眠,让出CPU。等待锁被唤醒适用于临界区正常的情况,以下两种不适应的情况
自旋锁 spinlock获取不到锁的时候,继续检测,空耗CPU适用于并发度不是特别高的场景,以及临界区比较短小的情况,利用避免线程切换来提高效率
无锁CAS依赖于CPU的原子性指令实现,在硬件上、寄存器上进行阻塞比较和交换,适用于队列放入和读取非常频繁时

互斥量

基本用法

以下代码通过lock和unlock去上锁和解锁

#include <iostream>
#include <mutex>
#include <thread>
#include <chrono>

using namespace std;

std::mutex g_lock;	// 声明一个锁	

void func()
{
	g_lock.lock();
	std::out << "entered thread " << std::this_thread::get_id() << std::endl;
	std::this_thread::sleep_for(std::chrono::seconds(1);
	std::out << "entered thread " << std::this_thread::get_id() << std::endl;
	std::this_thread::sleep_for(std::chrono::seconds(1);
	std::out << "entered thread " << std::this_thread::get_id() << std::endl;
	g_lock.unlock();
}

int main()
{
thread t1(func);
thread t2(func);
thread t3(func);

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

return 0;
}

 当然我们可以使用lock_guard来简化写法,该写法可以在作用区间内自动锁定互斥量,并在退出作用域自动释放。避免忘记了unlock操作。
 demo如下:

void func()
{
	std::lock_guard<std::mutex> lock(g_lock);
	std::out << "entered thread " << std::this_thread::get_id() << std::endl;
	std::this_thread::sleep_for(std::chrono::seconds(1);
	std::out << "entered thread " << std::this_thread::get_id() << std::endl;
	std::this_thread::sleep_for(std::chrono::seconds(1);
	std::out << "entered thread " << std::this_thread::get_id() << std::endl;
}

无锁 CAS

CAS(compare and swap):比较和交换,

  • 原型
bool __sync_bool_compare_and_swap(int *paddr, int nExpected, int new);
  • 描述
    通过比较paddr所指向内存的值是否与期望值(nExpected)是否一致,从而决定是否替换为new值;一致则替换成new值。

  • demo

int oldNum = 2;
do
{
oldNum ++;
}while(! __sync_bool_compare_and_swap(&oldNum, 3, 2))

死锁和避免死锁

死锁

 死锁不是锁的一种,死锁指的是两个或多个进程(线程)在执行过程中,因争夺资源或彼此通信而导致的一种互相等待的现象。
 比如同一个线程中使用多个锁的时候,例如

#include <iostream>
#include <mutex>

using namespace std;

std::mutex mutex;
int max(int a, int b)
{
	lock_guard<mutex> lock(mutex);
	return a > b ? a : b;
}

void testMax()
{
	lock_guard<mutex> lock(mutex);
	max(5, 6);
	return 0;
}

void main()
{
	testMax();
}

以上,在调用testMax时将会出现死锁,这称为嵌套锁。再比如本身有个锁还相互望着对方的锁的时候。
在这里插入图片描述

产生死锁的四个条件

  • 互斥条件:在一段时间内某资源只由一个进程占用
  • 请求与保持条件:一个线程因请求资源而阻塞时,对已获得的资源保持不放。
  • 不剥夺条件:线程已获得的资源在未使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。
  • 循环等待条件:在发生死锁时,必然存在一个进程——资源的环形链,在循环的等待资源

避免死锁

如何预防和避免线程死锁?

  • 破坏请求与保持条件 :一次性申请所有的资源。
  • 破坏不剥夺条件 :占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。
  • 破坏循环等待条件 :靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放。

避免死锁的实际方法包括但不限于以下几种

  1. 避免使用共享资源,定期释放锁。
  2. 线程按照一定的顺序来申请多个资源,按照固定的顺序申请和释放锁。
  3. 避免嵌套锁,即避免在一个已经持有锁的代码块中再次获取锁。
  4. 使用锁的分级策略,先申请低级别的锁,再申请高级别的锁。
  5. 使用死锁检测和恢复机制,检测是否发生死锁,如果发生,则采取措施恢复程序的执行,例如释放不必要的锁或重新调度线程等。

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

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

相关文章

SQL获取最后一次的数据

问题 有个表格(id,machineName,value,updatetime)&#xff0c;里面比如有10个机台&#xff0c;里面记录了这10个机台的几十万条数据 如何获取每个机台的最后一笔数据&#xff1f; machines表 解决办法 1.首先获得每个机台最后的更新时间 select machineName,max(updatetim…

常见的工业路由器访问问题

A&#xff1a;工业路由器已经设置了pptp怎么访问路由下面的电脑 1. 确认PPTP VPN设置&#xff1a;首先&#xff0c;确保PPTP VPN服务器在工业路由器上已正确设置&#xff0c;并且处于活动状态。这包括确保VPN服务器的IP地址、端口、用户名和密码等设置正确无误。 2. 连接到VP…

【各大网站独家代理】腾讯应用宝腾讯王者荣耀残棋方式拉新生态,轻轻松松日如1000

全新生态项目独家首发&#xff0c;自4月15日起全面启动&#xff01;现在正是时候加入&#xff0c;抢占蓝海市场&#xff0c;赚取第一桶金&#xff01; 下载 地 址 &#xff1a; laoa1.cn/1789.html 腾讯王者荣耀残局挑战拉新项目&#xff1a; 引流成本为8元/单&#xff0c;…

static和extern关键字详解

目录 创作不易&#xff0c;如对您有帮助&#xff0c;还望一键三连&#xff0c;谢谢&#xff01;&#xff01;&#xff01; 回顾 1.作用域和声明周期 1.1作用域 1.2生命周期 2.static和extern 2.1extern 2.2static 2.2-1static修饰局部变量 2.2-2static修饰全局变量 创…

k8s使用calico网络插件时,集群内节点防火墙策略配置方法

前言 我们在内网使用k8s时&#xff0c;有时候需要针对整个集群的节点设置防火墙&#xff0c;阻止一些外部访问&#xff0c;或者是仅允许白名单内的ip访问&#xff0c;传统做法是使用firewall之类的防火墙软件&#xff0c;但是&#xff0c;使用firewall存在如下问题&#xff1a…

资料总结分享:数据库:1.设计概念

目录 1 数据库设计任务 3 数据库设计方法 4 数据库设计的基本步骤 4.1 需求分析阶段 4.2 概念结构设计阶段 4.3 逻辑结构设计阶段 4.4 物理结构设计 4.5 数据库的实施阶段 4.6 数据库的运行和维护阶段 5 数据库结构设计阶段 6 数据库设计与模式结构 根据一个单位的信…

比特币减半倒计时:NFT 生态将受到怎样的影响?

BTC 减半倒计时仅剩不到 1 天&#xff0c;预计在 4 月 20 日迎来减半。当前区块奖励为 6.25 BTC&#xff0c;减半后区块奖励为 3.125 BTC&#xff0c;剩余区块为 253。比特币减半无疑是比特币发展史上最重要的事件之一&#xff0c;每当这一事件临近&#xff0c;整个加密社区都充…

qt tcp 连接 秒断连

问题&#xff1a; tcp连接总是秒成功后断连 debug会出现下面这些 onecore\net\netprofiles\service\src\nsp\dll\namespaceserviceprovider.cpp(550)\nlansp_c.dll!00007FFDA2A1D93D: (caller: 00007FFDD8BEACF6) LogHr(1) tid(336c) 8007277C ¡£¡£ one…

Python交换两个变量的值

问题 python交换两个变量的值很简单&#xff1a; x, y y, x 复制号右侧的元祖会首先计算内部的值&#xff0c;然后安装从左到右的顺序&#xff0c;依次赋值给赋值号右侧的变量。 有一种情况需要注意&#xff1a; 如图&#xff0c;交换下标为0的数字2和下标为2的数字4。返回…

在React函数组件中使用错误边界和errorElement进行错误处理

在React 18中,函数组件可以使用两种方式来处理错误: 使用 ErrorBoundary ErrorBoundary 是一种基于类的组件,可以捕获其子组件树中的任何 JavaScript 错误,并记录这些错误、渲染备用 UI 而不是冻结的组件树。 在函数组件中使用 ErrorBoundary,需要先创建一个基于类的 ErrorB…

加州大学欧文分校英语中级语法专项课程01:Perfect Tenses and Modals 学习笔记

Perfect Tenses and Modals course certificate 本文是学习https://www.coursera.org/learn/perfect-tenses-modals?specializationintermediate-grammar 这门课的学习笔记&#xff0c;如有侵权&#xff0c;请联系删除。 这门课是如下专项中的一门&#xff1a;Learn English:…

【前端】3. CSS【万字长文】

CSS 是什么 层叠样式表 (Cascading Style Sheets). CSS 能够对网页中元素位置的排版进行像素级精确控制, 实现美化页面的效果. 能够做到页面的样式和结构分离. CSS 就是 “东方四大邪术” 之化妆术. 基本语法规范 选择器 {一条/N条声明} 选择器决定针对谁修改 (找谁)声明决…

Hadoop——Yarn基础架构

Hadoop——Yarn基础架构 Hadoop YARN&#xff08;Yet Another Resource Negotiator&#xff09;是Apache Hadoop生态系统中的一个子项目&#xff0c;它是用于集群资源管理的框架&#xff0c;负责为运算程序提供服务器运算资源&#xff0c;相当于一个分布式的操作系统平台&…

前端发送请求,显示超时取消

前端发送请求&#xff0c;显示超时取消 问题说明&#xff1a;后台接口请求60s尚未完成&#xff0c;前端控制台显示取消&#xff08;canceled&#xff09; 原因 1、前端设置60s超时则取消 2、后台接口响应时间过长&#xff0c;过长的原因统计的数据量多&#xff08;实际也才17…

Docker的数据管理、网络通信和dockerfile

目录 一、Docker的数据管理 1. 数据卷 1.1 数据卷定义 1.2 数据卷配置 2. 数据卷容器 2.1 创建数据卷容器 2.2 使用--volume-from来挂载test1 二、端口映射 三、容器互联 1. 创建容器互联 ​编辑2. 进入test2测试&#xff08;ping 容器名/别名&#xff09; 四、Dock…

进制转换问题

1.十进制转二进制&#xff08;善于使用__int128&#xff09; 3373. 进制转换 - AcWing题库 #include<bits/stdc.h> using namespace std; __int128 x; int x_; string s1; int main(){stack<int> s;while(cin>>s1){int lens1.size();for(int i0;i<len;i)…

【原创教程】EPLAN如何制作专属的封面

想要给EPLAN制作专属封面吗?没问题,我来给你支个招。在EPLAN设计电气图纸时,封面就是第一印象,得好好弄。咱们以口罩机项目为例,来看看怎么做吧! 首先,得新建个封面。在项目属性里找到表格名称,点那个数值下拉菜单,选择“查找”。在弹出的表格里挑个你喜欢的模版,点击…

jmeter5.4.1源码编译(IDEA)问题解决

问题现象&#xff1a;最近想更深入的研究下jmeter5.4.1的原理及功能具体实现&#xff0c;从官网down了个源码&#xff0c;在本地使用IDEA工具导入项目、编译时&#xff0c;报以下错误&#xff1a; class jdk.internal.loader.ClassLoaders$PlatformClassLoader cannot be cast…

P44,45 属性预处理,执行后游戏效果回调,附录指定区域内修改变量

这节课主要是怎么对Attribute进行在进行到游戏角色前先进行处理,以及游戏效果如何回调 AuraAttributeSet.h // Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h" #include "AttributeSet.h&…

前端常用样式组元SCSS

* { margin: 0; padding: 0; border: 0; box-sizing: border-box; } #主题色 $primary: #183ee4; $success: #0cce63; $danger: #f00c63; mixin setThemeBgColor($name, $oClor, $start, $end) { .#{$name}-color { color: $oClor } .#{$name}-color-active { color: dark…