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

[Linux]互斥锁(什么是锁,为什么需要锁,怎么使用锁(接口),演示代码)

目录

 

一、锁的概念

一些需要了解的概念

什么是锁?为什么需要锁?什么时候使用锁?怎么定义锁?

二、锁的接口

1.初始化锁

2.加锁

3.申请锁

4.解锁

5.销毁锁

三、实践(写代码):黄牛抢票

Makefile

test.cc

结果


 

一、锁的概念

一些需要了解的概念

  • 临界资源:任一时刻只允许一个线程访问的共享资源
  • 临界区:访问临界资源的代码
  • 原子性:不会被任何调度机制打断的操作,该操作只有两态:要么完成,要么未完成
  • 互斥:任何时刻,互斥可以保证有且只有一个执行流进入临界区,访问临界资源,通常对临界资源起保护作用,而锁就是实现互斥的。
  • 同步:同步是一种机制,用于协调不同进程、线程或设备之间的操作,确保它们按照预期的顺序和方式进行。同步的目的是保持数据的一致性和系统的稳定性。

什么是锁?为什么需要锁?什么时候使用锁?怎么定义锁?

什么是锁?

锁是一种同步机制,用于控制多个线程对共享资源的访问。通过锁,可以确保一次只有一个线程能够访问特定的代码段或数据,从而防止数据竞争和不一致。锁的主要目的是确保数据的一致性和线程安全性。

为什么需要锁?

需要锁的主要原因在于确保多线程或多用户环境中共享资源访问的原子性和数据一致性。在多线程应用中,若多个线程同时访问并修改同一资源,可能导致数据冲突、不一致甚至损坏。

故事说明:把线程比作一个学生,小明,锁是自习室的门口上的锁头,当小明要进自习室时,他就从墙上拿钥匙(钥匙只有一把)解锁进入教室,而此时的自习室就是临界区,小明在自习室的书本,本子,笔就是临界资源,当小明突然要上厕所离开自习室时,因为自习室里有小明的书本呀,笔呀,等等东西,所以小明离开时就把门锁上了。小明自习了一天了,到晚上了吃饭了,小明不想自习了,小明带上他的东西离开,然后 把门锁上,把钥匙挂回墙上,此时其他同学就可以使用自习室了。在这例子中锁的作用就是只允许有钥匙的学生进入自习室,不允许其他没有钥匙的同学进入(其他线程),换言之锁的作用就是实现在一个临界区中的任一时刻只允许一个线程进入,访问。在这个故事中如果自习室门上没有锁,当小明要上厕所时,别的同学可以可以进入自习室破坏,拿走小明的东西呢?答案是有可能的,所以为了保证自习室里的小明的资源的安全,所以需要锁,把门上锁。

什么时候使用锁?

使用锁主要在多线程或多用户环境中,当多个线程或用户需要并发访问和修改共享资源时。锁能确保同一时间只有一个线程或用户访问资源,避免数据冲突和不一致。在需要保证数据完整性、原子性和安全性的场景下,应使用锁来同步和控制对共享资源的访问。

在Linux中锁长什么样呢?我们怎么定义锁呢?

pthread_mutex_t是一个用于线程同步的互斥锁类型。例如:pthread_mutex_t mutex:定义互斥锁变量mutex,mutex就是一个锁(锁变量)。

二、锁的接口

1.初始化锁

 

功能:用于初始化锁变量
原型

#include <pthread.h>

锁为局部变量时使用如下函数初始化

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

锁为全局变量时使用如下方法初始化

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
 

参数

  1. pthread_mutex_t *restrict mutex:指向要初始化的互斥锁变量的指针。这个指针指向的互斥锁变量在调用 pthread_mutex_init 之前应该是未初始化的。

  2. const pthread_mutexattr_t *restrict attr:指向互斥锁属性的指针。这个参数是可选的,通常可以传递 NULL (nullptr)来使用默认的互斥锁属性。如果你需要设置特定的属性(例如互斥锁的类型),你需要先使用 pthread_mutexattr_init 初始化一个 pthread_mutexattr_t 变量,然后设置所需的属性,最后将其传递给 pthread_mutex_init

返回值:成功返回0;失败返回错误码

使用例子:

#include <iosteam>
#include <pthread.h>
using namespace std;//全局锁初始化方式
pthread_mutex_t global_mutex = PTHREAD_MUTEX_INITIALIZER;//定义并初始化锁int main()
{//局部初始化方式pthread_mutex_t local_mutex;//定义锁pthread_mutex_init(&local_mutex, nullptr);//初始化锁//加锁//...//解锁//销毁锁return 0;
}

 

2.加锁

功能:获取(锁定)互斥锁,如果锁当前未被其他线程占用(即锁是“空闲”的),那么调用此函数的线程将成功获取锁,并可以继续执行其临界区代码。如果锁已被其他线程占用,则调用线程将被阻塞,直到锁被释放(即被当前持有锁的线程调用 pthread_mutex_unlock
原型

#include <pthread.h>

int pthread_mutex_lock(pthread_mutex_t *mutex);

参数

pthread_mutex_t *mutex:是一个指向互斥锁变量的指针。

返回值

pthread_mutex_lock 函数的返回值用于指示加锁操作是否成功。

  • 如果成功获取锁,函数返回 0
  • 如果在尝试获取锁时发生错误(例如,由于无效的互斥锁指针或系统资源不足),函数将返回一个错误码(非零值)。具体的错误码可以根据不同的系统和库实现而有所不同,但通常会遵循 POSIX 线程标准中定义的错误码。

注意事项

  • 使用 pthread_mutex_lock 时,必须确保在不再需要锁时调用 pthread_mutex_unlock 来释放锁,以避免死锁。
  • 如果在调用 pthread_mutex_lock 后线程被中断或取消,锁可能仍然处于锁定状态,需要特别小心处理。
  • 如果互斥锁的类型是递归锁(recursive mutex),则同一个线程可以多次获取同一个锁,但每次获取锁后都必须对应地释放锁。

3.申请锁

功能:尝试获取一个互斥锁(mutex),而不会阻塞调用线程。如果互斥锁已经被其他线程持有,则 pthread_mutex_trylock 不会使调用线程进入睡眠状态等待锁释放,而是立即返回表示失败的错误码。
原型

#include <pthread.h>

int pthread_mutex_trylock(pthread_mutex_t *mutex);
参数

pthread_mutex_t *mutex:是一个指向互斥锁变量的指针。
返回值

  • 如果成功获取锁,函数返回 0
  • 如果锁已被其他线程占用,或者发生其他错误(如传递了无效的互斥锁指针),函数将返回一个错误码。常见的错误码包括 EBUSY(表示锁当前被其他线程占用)和 EINVAL(表示传递给函数的互斥锁是无效的)。

4.解锁

功能:用于释放互斥锁(mutex),在多线程编程中用于确保线程同步的正确性。当一个线程完成对共享资源的访问后,它应该调用 pthread_mutex_unlock 来释放锁,以便其他线程能够获取该锁并访问相同的资源。
原型

#include <pthread.h>

int pthread_mutex_unlock(pthread_mutex_t *mutex);

参数

pthread_mutex_t *mutex:是一个指向互斥锁变量的指针。
返回值

  • 如果成功释放锁,函数返回 0
  • 如果在尝试解锁时发生错误(例如,传递给函数的互斥锁是无效的,或者当前线程没有持有该锁),函数将返回一个错误码。常见的错误码包括 EINVAL(表示传递给函数的互斥锁是无效的)和 EPERM(表示当前线程没有持有该锁)。

5.销毁锁

功能:用于销毁(释放)互斥锁(mutex)的函数。在多线程编程中,当不再需要某个互斥锁时,应该调用 pthread_mutex_destroy 来销毁它,以释放相关资源。
原型

#include <pthread.h>

int pthread_mutex_destroy(pthread_mutex_t *mutex);

参数

pthread_mutex_t *mutex:是一个指向互斥锁变量的指针。
返回值

  • 如果成功销毁锁,函数返回 0
  • 如果在尝试销毁锁时发生错误(例如,传递给函数的互斥锁是无效的,或者锁仍被持有),函数将返回一个错误码。常见的错误码包括 EBUSY(表示锁当前被其他线程占用)和 EINVAL(表示传递给函数的互斥锁是无效的)。

三、实践(写代码):黄牛抢票

说明:用多线程模拟黄牛,常数模拟票

Makefile

test:test.ccg++ -o $@ $^ -std=c++11 -lpthread
PHONY:clean
clean:rm -f test;

test.cc

#include <iostream>
#include <string>
#include <unistd.h>
#include <pthread.h>
#include <vector>
using namespace std;
int ticket = 1000; // 一千张票
int threadnum = 5; // 黄牛数
// 定义全局锁
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *threadtask(void *args)
{while (ticket > 0){string name = static_cast<char *>(args);// 加锁pthread_mutex_lock(&mutex);cout << "我是: " << name << "我抢到了" << ticket << "号票" << endl;ticket--; // 票数--sleep(1);// 解锁pthread_mutex_unlock(&mutex);}return nullptr;
}
int main()
{// 五个黄牛vector<pthread_t> threads;for (int i = 0; i < threadnum; i++){pthread_t tid;char threadname[64];sprintf(threadname, "thraed-%d", i + 1);pthread_create(&tid, nullptr, threadtask, threadname);threads.push_back(tid);}void *ret;for (int i = 0; i < threads.size(); i++){pthread_join(threads[i], &ret);//等待线程,回收资源}// 销毁锁pthread_mutex_destroy(&mutex);return 0;
}

结果

全是5号线程抢到票,原因是Linux内核中的线程调度器根据线程的优先级、状态和其他因素来决定哪个线程应该被调度执行。如果某个线程的优先级高于其他线程,或者其状态更适合执行(例如,它已准备好运行并且没有受到阻塞),那么它就更有可能被调度器选中。你可以在你的linux中试试或许会有不同的结果。

0c1a3fe630b84286af006feae9802cef.png

 完结!!!

 

相关文章:

[Linux]互斥锁(什么是锁,为什么需要锁,怎么使用锁(接口),演示代码)

目录 一、锁的概念 一些需要了解的概念 什么是锁&#xff1f;为什么需要锁&#xff1f;什么时候使用锁&#xff1f;怎么定义锁&#xff1f; 二、锁的接口 1.初始化锁 2.加锁 3.申请锁 4.解锁 5.销毁锁 三、实践&#xff08;写代码&#xff09;&#xff1a;黄牛抢票 M…...

Web基础06-AJAX,Axios,JSON数据

目录 一、AJAX 1.概述 2.主要作用 3.快速入门 4.AJAX的优缺点 &#xff08;1&#xff09;优点 &#xff08;2&#xff09;缺点 5.同源策略 二、Axios 1.概述 2.快速入门 3.请求方式别名 三、JSON 1.概述 2.主要作用 3.基础语法 4.JSON数据转换 &#xff08;1…...

Java 文件序列化和反序列化

list序列化 /*** 序列化* param list* param filename* throws IOException*/public static void serializeList(List<Map<String, Object>> list, String filename) throws IOException {try (ObjectOutputStream oos new ObjectOutputStream(new FileOutputStre…...

NETLINK_ROUTE 与 NETLINK_SOCK_DIAG 的区别与用法

在 Linux 中&#xff0c;Netlink 是一种用于内核与用户空间之间通信的机制&#xff0c;通过不同的 Netlink 协议族&#xff08;family&#xff09;可以实现不同类型的通信。其中&#xff0c;NETLINK_ROUTE 和 NETLINK_SOCK_DIAG 是两种常用的 Netlink 协议族&#xff0c;它们具…...

docker yocto vscode

scode的docker插件怎么使用 vscode是一个基于VS Code的开发环境&#xff0c;它提供了一个Docker插件&#xff0c;可以帮助你在VS Code中使用Docker进行开发。下面是使用scode的Docker插件的步骤&#xff1a; 安装scode插件&#xff1a;在VS Code的扩展商店中搜索并安装scode插…...

使用ansible剧本进行lvm分盘

使用 Ansible 剧本&#xff08;Playbook&#xff09;进行 LVM 分区管理是一种自动化的方式&#xff0c;可以帮助管理员在多台主机上批量管理逻辑卷。 部署环境 3台主机&#xff0c;添加硬盘 ansible-galaxy collection install community.general 联网执行&#xff0c;下…...

【排序】快速排序

原理 对于一个数组x&#xff0c;快速排序流程如下&#xff1a; 确定分界点a&#xff0c;可以取x[l]、x[r]、x[l r / 2]、随机&#xff08;四种都可以&#xff09;调整区间&#xff0c;使得&#xff1a;区间被分成 < a 和 > a的两部分&#xff0c;左边 < a&#xff…...

Python大数据实践:selenium爬取京东评论数据

准备工作 selenium安装 Selenium是广泛使用的模拟浏览器运行的库&#xff0c;用于Web应用程序测试。 Selenium测试直接运行在浏览器中&#xff0c;就像真正的用户在操作一样&#xff0c;并且支持大多数现代 Web 浏览器。 #终端pip安装 pip install selenium #清华镜像安装 p…...

信息系统项目管理师019:存储和数据库(2信息技术发展—2.1信息技术及其发展—2.1.3存储和数据库)

文章目录 2.1.3 存储和数据库1.存储技术2.数据结构模型3.常用数据库类型4.数据仓库 记忆要点总结 2.1.3 存储和数据库 1.存储技术 存储分类根据服务器类型分为&#xff1a;封闭系统的存储和开放系统的存储。封闭系统主要指大型机等服务器。开放系统指基于包括麒麟、欧拉、UNIX…...

Python基础(六)之数值类型元组

Python基础&#xff08;六&#xff09;之数值类型元组 1、简介 元组&#xff1a; 在Python中是内置的数据结构之一&#xff0c;是一个不可变的序列,切可以是任何类型数据。元组的元素放在&#xff08;&#xff09;小括号内。一般我们希望数据不改变的时候使用 不可变与可变的…...

Chrome历史版本下载地址:Google Chrome Older Versions Download (Windows, Linux Mac)

最近升级到最新版本Chrome后发现页面居然显示错乱,是在无语, 打算退回原来的版本, 又发现官方只提供最新的版本下载, 为了解决这个问题所有收集了Chrome历史版本的下载地址分享给大家. Google Chrome Windows version 32-bit VersionSizeDate104.0.5112.10279.68 MB2022-05-30…...

ROS2纯跟踪实现(C++)

#include <tf2_ros/buffer.h> #include <tf2_ros/transform_broadcaster.h> #include <tf2_ros/transform_listener.h>#include <geometry_msgs/msg/transform_stamped.hpp> #include...

uniapp微信小程序随机生成canvas-id报错?

uniapp微信小程序随机生成canvas-id报错&#xff1f; 文章目录 uniapp微信小程序随机生成canvas-id报错&#xff1f;效果图遇到问题解决 场景&#xff1a; 子组件&#xff0c;在 mounted 绘制 canvas&#xff1b;App、H5端正常显示&#xff0c;微信小程序报错&#xff1b; 效…...

爬虫 Day2

resp.close()#关掉resp 一requests入门 &#xff08;一&#xff09; 用到的网页&#xff1a;豆瓣电影分类排行榜 - 喜剧片 import requestsurl "https://movie.douban.com/j/chart/top_list" #参数太长&#xff0c;重新封装参数 param {"type": "…...

达梦数据库SQL

达梦JSON函数技术文档 SQL中关键词处理 -- 必须要使用双引号包裹 select id,"comment" from t_cmp_rd_process;select id,"commit" from t_cmp_rd_gjj_eva;JSON_EXTRACT函数 -- party_sup_other_json 是包含JSON数据的列名。 -- $.content_abstract 是J…...

python教程——把视频转成gif

一、前言 很多网站提供视频转GIF的功能&#xff0c;但要么收费要么有广告&#xff0c;实际上可以通过python&#xff0c;几行代码就能够实现视频转gif。 二、使用方法 1安装必备库moviepy pip install moviepy -i https://pypi.tuna.tsinghua.edu.cn/simple 2. 写入代码 …...

深入浅出Go的`encoding/xml`库:实战开发指南

深入浅出Go的encoding/xml库&#xff1a;实战开发指南 引言基本概念XML简介Go语言中的XML处理结构体标签&#xff08;Struct Tags&#xff09; 解析XML数据使用xml.Unmarshal解析XML结构体标签详解处理常见解析问题 生成XML数据使用xml.Marshal生成XML使用xml.MarshalIndent优化…...

深度学习之扩散模型(Diffusion model)

代码解析&#xff1a;正向扩散过程和加噪演示 引言 这段代码实现了一个正向扩散过程和加噪演示的功能。通过生成一个特定形状的数据集&#xff0c;并在每个时间步长上应用正向扩散过程和加噪过程&#xff0c;最终展示了数据点在空间中的演变过程。 数据集生成 通过 make_swiss…...

Tomcat Session ID---会话保持

简单拓补图 一、负载均衡、反向代理 7-1nginx代理服务器配置 [rootdlnginx ~]#yum install epel-release.noarch -y ###安装额外源[rootdlnginx ~]#yum install nginx -y[rootdlnginx ~]#systemctl start nginx.service[rootdlnginx ~]#systemctl status nginx.service [ro…...

Session会话绑定

1.需求原因 用户的请求,登录的请求,经过负载均衡后落到后面的web服务器上,登录的状态/信息也会记录在web服务器上,就会导致不通的web服务器上,登录状态不统一,造成用户频繁需要登录 2.目标&#xff1a;如何实现会话保持/会话共享 方案一&#xff1a;登录状态写入cookie中.(wor…...

Java 语言特性(面试系列2)

一、SQL 基础 1. 复杂查询 &#xff08;1&#xff09;连接查询&#xff08;JOIN&#xff09; 内连接&#xff08;INNER JOIN&#xff09;&#xff1a;返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...

【OSG学习笔记】Day 18: 碰撞检测与物理交互

物理引擎&#xff08;Physics Engine&#xff09; 物理引擎 是一种通过计算机模拟物理规律&#xff08;如力学、碰撞、重力、流体动力学等&#xff09;的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互&#xff0c;广泛应用于 游戏开发、动画制作、虚…...

前端开发面试题总结-JavaScript篇(一)

文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包&#xff08;Closure&#xff09;&#xff1f;闭包有什么应用场景和潜在问题&#xff1f;2.解释 JavaScript 的作用域链&#xff08;Scope Chain&#xff09; 二、原型与继承3.原型链是什么&#xff1f;如何实现继承&a…...

Java面试专项一-准备篇

一、企业简历筛选规则 一般企业的简历筛选流程&#xff1a;首先由HR先筛选一部分简历后&#xff0c;在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如&#xff1a;Boss直聘&#xff08;招聘方平台&#xff09; 直接按照条件进行筛选 例如&#xff1a…...

Reasoning over Uncertain Text by Generative Large Language Models

https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...

莫兰迪高级灰总结计划简约商务通用PPT模版

莫兰迪高级灰总结计划简约商务通用PPT模版&#xff0c;莫兰迪调色板清新简约工作汇报PPT模版&#xff0c;莫兰迪时尚风极简设计PPT模版&#xff0c;大学生毕业论文答辩PPT模版&#xff0c;莫兰迪配色总结计划简约商务通用PPT模版&#xff0c;莫兰迪商务汇报PPT模版&#xff0c;…...

【C++进阶篇】智能指针

C内存管理终极指南&#xff1a;智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...

pycharm 设置环境出错

pycharm 设置环境出错 pycharm 新建项目&#xff0c;设置虚拟环境&#xff0c;出错 pycharm 出错 Cannot open Local Failed to start [powershell.exe, -NoExit, -ExecutionPolicy, Bypass, -File, C:\Program Files\JetBrains\PyCharm 2024.1.3\plugins\terminal\shell-int…...

ubuntu22.04 安装docker 和docker-compose

首先你要确保没有docker环境或者使用命令删掉docker sudo apt-get remove docker docker-engine docker.io containerd runc安装docker 更新软件环境 sudo apt update sudo apt upgrade下载docker依赖和GPG 密钥 # 依赖 apt-get install ca-certificates curl gnupg lsb-rel…...

C++_哈希表

本篇文章是对C学习的哈希表部分的学习分享 相信一定会对你有所帮助~ 那咱们废话不多说&#xff0c;直接开始吧&#xff01; 一、基础概念 1. 哈希核心思想&#xff1a; 哈希函数的作用&#xff1a;通过此函数建立一个Key与存储位置之间的映射关系。理想目标&#xff1a;实现…...