《多线程基础之互斥锁》

【互斥锁导读】互斥锁是大家使用最多的线程同步手段,但仅仅知道怎么用还是不够的?比如:面试官问你"互斥锁是属于内核层还是应用层的同步保护机制?性能怎样?","频繁加解锁,会有什么问题?","死锁了,应该如何排查?"

     

      和锁相关的api,我就不介绍了,同学们只需要参考下锁使用的相关示例,就能很好掌握它的用法,好!现在直接上代码。 

#include <pthread.h>
#include <iostream>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
using namespace std;pthread_mutex_t myMutex;
int resourceNo = 0;void* workthread(void* param)
{pthread_t threadNo = pthread_self();while(true){pthread_mutex_lock(&myMutex);cout << "thread No: " << threadNo << " resource No: " << ++resourceNo << endl;pthread_mutex_unlock(&myMutex);sleep(1);}return NULL;   
}int main()
{cout << getpid() << endl;pthread_mutexattr_t mutexAttr;pthread_mutexattr_init(&mutexAttr);//设置互斥锁的类型,PTHREAD_MUTEX_ERRORCHECK表明是检错锁,//如果加锁失败,接口会返回对应的错误码pthread_mutexattr_settype(&mutexAttr,         PTHREAD_MUTEX_NORMAL | PTHREAD_MUTEX_ERRORCHECK);pthread_mutex_init(&myMutex, &mutexAttr);//创建5个线程pthread_t threadID[5];for (int i = 0; i < 5; ++i)pthread_create(&threadID[i], NULL, workthread, NULL);for (int i = 0; i < 5; ++i)pthread_join(threadID[i], NULL);//销毁锁资源pthread_mutex_destroy(&myMutex);pthread_mutexattr_destroy(&mutexAttr);return 0; 
}
 

    运行结果如下,5个线程分别去争抢全局的锁对象myMutex,进而保证resourceNo的递增符合线程安全的要求。

图片

    互斥锁使用起来很轻便,但是其实是有性能问题的,因为一个线程去争取一把锁对象,势必要从用户态切换到内核态,释放锁资源后又从内核态切回用户态。其性能相对于信号量、自旋锁、临界区(windows)这些线程同步机制更低。

1、死锁问题

    好,我们稍微修改下上述的示例代码,让程序运行的流程出现死锁,我们可以关注下进程如果出现了死锁,会有怎样的表现

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <iostream>
using namespace std;pthread_mutex_t myMutexA;
pthread_mutex_t myMutexB;
int resourceBNo = 0;
int resourceANo = 0;void* workthreadA(void* param)
{pthread_t threadNo = pthread_self();while(true){pthread_mutex_lock(&myMutexB);cout << "thread A: " << threadNo << " resource B No: " << ++resourceBNo << endl;//线程A占用锁B,不释放锁,随后去争夺锁Apthread_mutex_lock(&myMutexA);cout << "thread A" << " resource A No: " << ++resourceANo << endl;pthread_mutex_unlock(&myMutexA);}return NULL;   
}void* workThreadB(void* param)
{pthread_t threadNo = pthread_self();while (true){pthread_mutex_lock(&myMutexA);cout << "thread B: " << threadNo << " resource A No: " << ++resourceANo << endl;//线程B占用锁A,不释放,随后去争夺锁Bpthread_mutex_lock(&myMutexB);cout << "thread B " << " resource B No: " << ++resourceBNo << endl;pthread_mutex_unlock(&myMutexB);}return NULL;
}
  我们模拟死锁的场景,用gdb调试程序lock,果不其然,lock进程(进程号为9394,通过getpid()获取)卡住了,两个子线程分别争夺对方占用的锁。

图片

   使用top指令查看进程9394的状态如下:

图片

      但进程的状态是Sleep(任务字段S下面对应的值),让人有点意外,难道陷入死锁的进程,进程会陷入睡眠的状态?而且当前进程CPU消耗为0,物理内存占比也为0。看到线程最后卡死在__lll_lock_wait接口处,也就是说pthread_mutex_lock接口最终会走到__lll_lock_wait()接口处。
 

/* This function doesn't get included in libc.  */
#if IS_IN (libpthread)
void __lll_lock_wait (int *futex, int private)
{if (*futex == 2)lll_futex_wait(futex, 2, private); /* Wait if *futex == 2.*/while (atomic_exchange_acq (futex, 2) != 0)lll_futex_wait (futex, 2, private); /* Wait if *futex == 2.*/
}
#endif/* Wait while *FUTEXP == VAL for an lll_futex_wake call on FUTEXP.  */
#define lll_futex_wait(futexp, val, private) \lll_futex_timed_wait (futexp, val, NULL, private)#define lll_futex_timed_wait(futexp, val, timeout, private)  \lll_futex_syscall (4, futexp, \__lll_private_flag (FUTEX_WAIT, private), val, timeout)

       初步看了下源码,__lll_lock_wait会陷入无限循环的等待中去,因为最终传递给lll_futex_timed_wait接口的超时时间为NULL,直到和锁相关的futex状态变成预定值,也即其它线程释放了这把锁,更改了futex的状态。当前线程才会停止休眠等待,继续执行。

2、频繁加锁问题

    好,讨论下最后一个问题,频繁加锁会有什么问题,我们可以先用个小程序演示下。

#include <pthread.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
int main()
{pthread_mutex_t mymutex;pthread_mutexattr_t mutex_attr;pthread_mutexattr_init(&mutex_attr);pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_NORMAL);pthread_mutex_init(&mymutex, &mutex_attr);int ret = pthread_mutex_lock(&mymutex);printf("ret = %d\n", ret);ret = pthread_mutex_lock(&mymutex);printf("ret = %d\n", ret);pthread_mutex_destroy(&mymutex);pthread_mutexattr_destroy(&mutex_attr);return 0;
}

       针对普通的锁,反复对同一把锁进行加锁操作,linux下的表现就是阻塞当前进程或者线程,程序卡死__lll_lock_wait()接口处。

图片

       windows下呢?是会卡死吗?笔者可以先剧透下,windows下频繁加解锁,会引起crt库崩溃,中断当前进程,感兴趣的同学可以去实验下。

      所以锁底层的知识面还是很细的,需要深入剖析底层的源码,分析加解锁的流程,那么遇到锁一类的问题就能迎难而解了。

    

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

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

相关文章

【Rust自学】15.0. 智能指针(序):什么是智能指针及Rust智能指针的特性

喜欢的话别忘了点赞、收藏加关注哦&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 15.0.1 指针的基本概念 指针是一个变量在内存中包含的是一个地址&#xff0c;指向另一个数据。 Rust 中最常见的指针是引用&#xff0c…

Android Studio 正式版 10 周年回顾,承载 Androider 的峥嵘十年

Android Studio 1.0 宣发于 2014 年 12 月&#xff0c;而现在时间来到 2025 &#xff0c;不知不觉间 Android Studio 已经陪伴 Androider 走过十年历程。 Android Studio 10 周年&#xff0c;也代表着了我的职业生涯也超十年&#xff0c;现在回想起来依然觉得「唏嘘」&#xff…

Swing使用MVC模型架构

什么是MVC模式? MVC是一组英文的缩写,其全名是Model-View-Controller,也就是“模型-视图-控制器”这三个部分组成。这三个部分任意一个部分发生变化都会引起另外两个发生变化。三者之间的关系示意图如下所示: MVC分为三个部分,所以在MVC模型中将按照此三部分分成三…

1.Template Method 模式

模式定义 定义一个操作中的算法的骨架&#xff08;稳定&#xff09;&#xff0c;而将一些步骤延迟&#xff08;变化)到子类中。Template Method 使得子类可以不改变&#xff08;复用&#xff09;一个算法的结构即可重定义&#xff08;override 重写&#xff09;该算法的某些特…

arm-linux-gnueabihf安装

Linaro Releases windows下打开wsl2中的ubuntu&#xff0c;资源管理器中输入&#xff1a; \\wsl$gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz 复制到/home/ark01/tool 在 Ubuntu 中创建目录&#xff1a; /usr/local/arm&#xff0c;命令如下&#xff1a; …

【PyTorch】6.张量形状操作:在深度学习的 “魔方” 里,玩转张量形状

目录 1. reshape 函数的用法 2. transpose 和 permute 函数的使用 4. squeeze 和 unsqueeze 函数的用法 5. 小节 个人主页&#xff1a;Icomi 专栏地址&#xff1a;PyTorch入门 在深度学习蓬勃发展的当下&#xff0c;PyTorch 是不可或缺的工具。它作为强大的深度学习框架&am…

百度热力图数据获取,原理,处理及论文应用5

目录 0、数据简介0、示例数据1、百度热力图数据日期如何选择1.1、其他实验数据的时间1.2、看日历1.3、看天气 2、百度热力图几天够研究&#xff1f;部分文章统计3、数据原理3.1.1 ** 这个比较重要&#xff0c;后面还会再次出现。核密度的值怎么理解&#xff1f;**3.1.2 Csv->…

论文阅读(九):通过概率图模型建立连锁不平衡模型和进行关联研究:最新进展访问之旅

1.论文链接&#xff1a;Modeling Linkage Disequilibrium and Performing Association Studies through Probabilistic Graphical Models: a Visiting Tour of Recent Advances 摘要&#xff1a; 本章对概率图模型&#xff08;PGMs&#xff09;的最新进展进行了深入的回顾&…

安装zsh并美化

0 Zsh 是一种功能强大的 shell&#xff0c;通常用于替代默认的 Bash shell。它为命令行提供了更多的功能&#xff0c;例如自动补全、强大的模式匹配和主题支持等。 Oh My Zsh 是用于管理 Zsh 配置的框架。 powerlevel10k是样式&#xff0c;通过p10k configure脚本可以调节自己…

最新-CentOS 7 基于1 Panel面板安装 JumpServer 堡垒机

CentOS 7 基于1 Panel面板安装 JumpServer 堡垒机 一、前言二、设备要求三、环境要求四、安装4.1 环境安装4.2 JumpServer安装4.3 访问JumpServerWeb端&#xff0c;进行登录 五、登录Web控制台 一、前言 JumpServer是广受欢迎的开源堡垒机。运维必备神器&#xff01;JumpServe…

跨境数据传输问题常见解决方式

在全球化经济的浪潮下&#xff0c;跨境数据传输已然成为企业日常运营的关键环节。随着数字贸易的蓬勃发展和跨国业务的持续扩张&#xff0c;企业在跨境数据处理方面遭遇了诸多棘手难题。那么&#xff0c;面对这些常见问题&#xff0c;企业该如何应对&#xff1f;镭速跨境数据传…

OpenEuler学习笔记(十七):OpenEuler搭建Redis高可用生产环境

在OpenEuler上搭建Redis高可用生产环境&#xff0c;通常可以采用Redis Sentinel或Redis Cluster两种方式&#xff0c;以下分别介绍两种方式的搭建步骤&#xff1a; 基于Redis Sentinel的高可用环境搭建 安装Redis 配置软件源&#xff1a;可以使用OpenEuler的默认软件源&#…

【编译原理实验二】——自动机实验:NFA转DFA并最小化

本篇适用于ZZU的编译原理课程实验二——自动机实验&#xff1a;NFA转DFA并最小化&#xff0c;包含了实验代码和实验报告的内容&#xff0c;读者可根据需要参考完成自己的程序设计。 如果是ZZU的学弟学妹看到这篇&#xff0c;那么恭喜你&#xff0c;你来对地方啦&#xff01; 如…

我的2024年博客总结(在工作、博客和生活中找到自己的生活节奏)

文章目录 ⭐前言⭐工作和博客的关联⭐找到自己的生活节奏⭐结束 ⭐前言 大家好&#xff0c;我是yma16&#xff0c;本文主要写2024年博客总结&#xff0c;关于在工作、博客和生活中找到自己的生活节奏。 node系列往期文章 node_windows环境变量配置 node_npm发布包 linux_配置…

RDK X5运行DeepSeek-R1-Distill-Qwen-1.5B,体验长思维链的语言大模型!

简介 本文介绍了在RDK X5上&#xff0c;如何从HuggingFace的原始模型权重&#xff08;safetensors&#xff09;经过量化和编译&#xff0c;的到llama.cpp推理框架所需要的GGUF格式的模型&#xff0c;然后演示了如何使用llama.cpp运行量化后的DeepSeek-R1-Distill-Qwen-1.5B模型…

巴塞尔问题详解:计算所有正整数平方的倒数之和

1 相关历史背景 巴塞尔问题&#xff08;Basel Problem&#xff09;是数学史上一个著名的问题&#xff0c;由意大利数学家皮埃特罗门戈利&#xff08;Pietro Mengoli&#xff09;在1644年首次提出。 但他未能解决&#xff0c;只能给出小数点后六位的近似解是1.644934&#xff0…

神经网络和深度学习

应用 类型 为什么近几年飞速发展 数据增长&#xff0c;算力增长&#xff0c;算法革新 逻辑回归 向量化 浅层神经网络(Shallow neural network) 单条训练数据前向传播计算表达式 batch训练数据前向传播计算表达式 反向传播计算表达式 参数随机初始化 不能全部设为0 原因是同一…

python3+TensorFlow 2.x(三)手写数字识别

目录 代码实现 模型解析&#xff1a; 1、加载 MNIST 数据集&#xff1a; 2、数据预处理&#xff1a; 3、构建神经网络模型&#xff1a; 4、编译模型&#xff1a; 5、训练模型&#xff1a; 6、评估模型&#xff1a; 7、预测和可视化结果&#xff1a; 输出结果&#xff…

AI大模型开发原理篇-8:Transformer模型

近几年人工智能之所以能迅猛发展&#xff0c;主要是靠2个核心思想&#xff1a;注意力机制Attention Mechanism 和 Transformer模型。本次来浅谈下Transformer模型。 重要性 Transformer模型在自然语言处理领域具有极其重要的地位&#xff0c;为NLP带来了革命性的突破‌。可以…

探索性测试与自动化测试的结合

随着软件开发周期的不断缩短和质量要求的不断提高&#xff0c;测试行业正在经历一场深刻的变革。自动化测试因其高效性和可重复性成为测试团队必不可少的工具&#xff0c;而探索性测试&#xff08;Exploratory Testing, ET&#xff09;则因其灵活性和创意性在面对复杂、动态变化…