当前位置: 首页 > news >正文

linux21 线程同步--互斥锁

学习互斥锁之前我们引入以下问题:

我们创建五个线程分别对val进行++打印1000次:

#include<stdio.h>#include<pthread.h>#include<unistd.h>#include<stdlib.h>#include<semaphore.h>int val=0;void* fun(void*arg){for(int i=0;i<10000;i++){val++;printf("val=%d\n",val);}}int main(){pthread_t id[5];int i=0;for(;i<5;i++){pthread_create(&id[i],NULL,fun,NULL);}for(i=0;i<5;i++){pthread_join(id[i],NULL);}exit(0);}

运行结果:

我们可以通过观察发现次代码并没有符合我们的预期打印50000,这是因为多个线程同时对val++造成的,为了解决这种现象我们利用互斥锁来来进行控制。

13.3.1互斥锁的基本概念

互斥锁是最常用的线程同步机制,用于保证在任意时刻只有一个线程可以访问特定的资源

  1. 竞态条件:当多个线程同时读写共享资源,且最终结果依赖于线程执行的先后顺序时,会出现不可预测的错误(如两个线程同时给计数器加 1,结果可能少加一次)。
  1. 互斥锁:本质是一个 “锁”,通过独占访问机制,保证同一时间只有一个线程能进入 “临界区”(操作共享资源的代码段),其他线程需等待锁释放后才能进入。

核心特性:

  • 原子性:锁定操作是原子的,不会被中断
  • 唯一性:一个线程锁定后,其他线程无法锁定,直到解锁
  • 非繁忙等待:等待锁的线程会被挂起,不消耗CPU资源

13.3.2互斥锁的工作原理

互斥锁的状态有两种:未锁定(解锁)锁定(持有)。其工作流程如下:

  1. 线程进入临界区前,尝试获取锁( lock ):
  • 若锁处于 “未锁定” 状态,线程获取锁并将其设为 “锁定” 状态,进入临界区执行操作。
  • 若锁处于 “锁定” 状态,线程会阻塞等待(进入休眠或循环检测),直到锁被释放。
  1. 线程离开临界区时,必须释放锁( unlock ),将锁设为 “未锁定” 状态,唤醒其他等待该锁的线程。

13.3.3互斥锁相应函数

函数原型

功能说明

 int pthread_mutex_init(  pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) 

初始化互斥锁( attr 为属性,通常设为 NULL 使用默认属性)。

 int pthread_mutex_lock(  pthread_mutex_t *mutex) 

获取锁:若锁未被持有,则锁定并返回;否则阻塞等待。

 int pthread_mutex_trylo  ck(pthread_mutex_t *mutex) 

尝试获取锁:若锁未被持有,则锁定并返回 0;否则立即返回非 0(不阻塞)。

 int pthread_mutex_unloc  k(pthread_mutex_t *mutex) 

释放锁:将锁设为未锁定状态,唤醒等待的线程。

 int pthread_mutex_destr  oy(pthread_mutex_t *mutex) 

销毁互斥锁,释放资源。

1.初始化函数

静态初始化:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

动态初始化:


pthread_mutex_t mutex;pthread_mutex_init(&mutex, NULL); // NULL表示使用默认属性

2.加锁解锁函数:

pthread_mutex_t mutex;pthread_mutex_lock(&mutex);加锁pthread_mutex_unlock(&mutex);解锁

13.3.4关键注意事项

  1. 锁的粒度
  • 临界区应尽可能小(只包含操作共享资源的代码),避免长时间持有锁导致其他线程阻塞,降低并发效率。
  • 反例:在锁内包含睡眠( sleep )、IO 操作等耗时任务,会严重影响性能。
  1. 避免死锁:死锁是指两个或多个线程互相等待对方释放锁,导致永久阻塞。常见原因及避免方法:
  • 循环等待:线程 A 持有锁 1 并等待锁 2,线程 B 持有锁 2 并等待锁 1。解决:所有线程按固定顺序获取锁(如先锁 1 后锁 2)。
  • 忘记释放锁:线程获取锁后因异常退出(如未处理的信号),未释放锁。解决:用 “资源获取即初始化(RAII)” 模式(C++ 中常用),在对象析构时自动释放锁。
  1. 递归锁与非递归锁
  • 默认互斥锁是非递归锁:同一线程多次获取同一锁会导致死锁(自身阻塞)。
  • 若需同一线程多次获取锁(如递归函数中操作共享资源),需使用递归锁(通过属性  PTHREAD_MUTEX_RECURSIVE  初始化)。
  1. 互斥锁与信号量的区别
  • 互斥锁强调 “独占”,通常用于保护临界区,所有权明确(谁获取谁释放)。
  • 信号量强调 “计数”,可用于控制多个线程同时访问资源(如限制 5 个线程同时读写文件)。

13.3.5总结

我们对以上代码进行修改:


#include<stdio.h>#include<pthread.h>#include<unistd.h>#include<stdlib.h>#include<semaphore.h>int val=0;pthread_mutex_t mutex;void* fun(void*arg){for(int i=0;i<10000;i++){pthread_mutex_lock(&mutex);val++;pthread_mutex_unlock(&mutex);printf("val=%d\n",val);}}int main(){pthread_t id[5];int i=0;pthread_mutex_init(&mutex,NULL);for(;i<5;i++){pthread_create(&id[i],NULL,fun,NULL);}for(i=0;i<5;i++){pthread_join(id[i],NULL);}pthread_mutex_destroy(&mutex);exit(0);}

运行结果:

可以观察到使用信号量可以达到预期值50000.

http://www.dtcms.com/a/542020.html

相关文章:

  • 建设网站的申请信用卡分期付款jsp做的网站代码
  • 致同研究:可变对价的披露示例
  • 做会员卡的网站在线制作海宁市建设局官方网站6
  • 图神经网络入门:用 MLP 作为 Cora 数据集的基线模型
  • 邢台建设银行网站网站挂载
  • 主要的网站开发技术路线网站投放
  • 金昌做网站秦皇岛优化seo
  • 短剧小程序开发的技术新蓝海:交互、社交与AIGC的落地实践
  • 鹤岗做网站怎么把文件发送到网站
  • 2025年--Lc220--589. N 叉树的前序遍历(递归版)-Java版
  • 网站制作好吗上海影视公司
  • 网站公司怎么做的好兴隆大院网站哪个公司做的
  • JS睡眠函数(JS sleep()函数、JS单线程、Event Loop事件循环)假睡眠
  • Windows配置解压版MySQL5(免安装)
  • 营销网站建设阿凡达平面设计主要做什么
  • 有什么好的网站设计思想的博客张掖高端网站建设公司
  • 公司网上注册在哪个网站做产品网站营销推广
  • 网站在线留言的用途建设专业网站哪家技术好
  • 做阀门网站效果怎么样网站建设-部署与发布
  • 达梦数据库笔记--分权
  • 网站建设php书籍免费制作微信小程序的网站
  • 潍城区住房和城乡建设局网站frontpage新建网站
  • 台州企业网站制作公司松江建设投资有限公司网站
  • 如何制作自己的网站教程自建国际网站做电商
  • 队列-概念【Queue1】
  • OCR用于Llamaindex与OCR运用的展望
  • DeepSeek-OCR MoE结构梳理(其它LLM原理类似)
  • 浙江建设银行网站多少钱算有钱
  • 青岛正规公司网站建设公司国内网如何看国外网站
  • 商城网站建设公司报价如何搜索公司所有的网站