Linux - 线程互斥和互斥锁

文章目录

  • 前言
  • 一、为什么要线程互斥
    • 原子性
  • 二、互斥锁
    • 互斥锁的创建与销毁
    • 互斥锁进行互斥


前言

前几节课,我们学习了多线程的基础概念,这节课,我们来对线程互斥和互斥锁的内容进行学习。


一、为什么要线程互斥

首先我们要明白,对于多线程,其实就是多个执行流在同时执行各自的代码。 而有的时候,我们多个执行流可能会同时访问到同一份资源,我们称这种资源叫做临界资源,而我们各个执行流访问这些临界资源的代码,就叫做临界区

当我们多个执行流访问临界资源时,就可能由于OS的线程时间调度问题,导致临界资源出现紊乱问题,所以,对于这种情况,我们就需要线程互斥来保护临界资源。

示例代码

#include <iostream>
#include <unistd.h>
#include <cstdio>
#include <pthread.h>
#include <string>
#define TNUM 5int ticket = 10000;void *grab(void *args)
{const std::string name = (char *)args;while (1){// --- 临界区if (ticket > 0){usleep(1000);printf("%s : sells ticket:%d\n", name.c_str(), ticket);ticket--;// --- 临界区}else{break;}}return nullptr;
}int main()
{pthread_t tid[TNUM];for (int i = 0; i < TNUM; i++){char name[64];snprintf(name, sizeof name, "new thread %d", i + 1);pthread_create(tid + i, nullptr, grab, (void *)name);usleep(10000);}for (int i = 0; i < TNUM; i++){pthread_join(tid[i], nullptr);}return 0;
}

如同此代码,这是一个抢票系统的简易代码,五个线程都执行抢票代码,对全局变量ticket进行–操作。

这串代码看上去似乎没有问题,但是在多线程的情况下,就可能会导致问题。
在这里插入图片描述
可是为什么呢? 我们的if判断不是如果ticket<0就break吗?

这是因为,我们的–操作在汇编角度,其实是三条语句。

第一步是将内存中的ticket move 到 寄存器中
第二步才是进行计算操作
第三步是将新计算的ticket move 到内存中

而操作系统在进行线程调度的时候,可不是管你是进行到第一步,可能你才刚执行完第一步,你的时间片就到了,然后你就被操作系统丢到运行队列末尾了,这就可能会导致临界资源不正常。

原子性

解答这个问题,就需要提出一个概念。叫做原子性。
原子性就是 我们要么不做,要么就把这件事做完,而通常而言,我们可以理解为一条汇编就是原子性的。

二、互斥锁

对于多线程库pthread,也必然会考虑到上面这种问题,所以就设计了互斥锁来保护我们的临界资源!

man 3 pthread_mutex_destroy
man 3 pthread_lock
man 3 pthread_unlock

互斥锁的创建与销毁

man 3 pthread_mutex_init
int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

pthread_mutex_t 就是pthread库给我们提供的锁的数据结构。

参数pthread_mutex_t* restrict_mutex 是我们需要初始化的锁。
参数const pthread_mutexattr_t *restrict attr 我们这里不做考虑,设为nullptr即可。

需要注意的是 对于mutex互斥锁的创建和初始化有两种。

第一种是对于静态或全局的pthread_mutex_t ,我们可以使用 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 这种方式直接进行初始化,而不需要调用pthread_mutex_init函数,也不需要再调用 pthread_mutex_destroy进行销毁

第二种是对于局部的pthread_mutex_t ,我们就必须要调用 pthread_mutex_init来进行初始化,最后再调用 pthread_mutex_destroy进行销毁。

互斥锁进行互斥

man 3 pthread_mutex_lock
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);

将mutex 理解为一把锁,且这把锁只有一把钥匙

pthread_mutex_lock函数是申请锁的钥匙,申请到了锁的钥匙则可以继续向下执行,如果没有申请到,则挂起等待。

pthread_mutex_unlock函数就是归还钥匙。

在这里插入图片描述
这是lock的伪代码,意思就是将0放入到寄存器%al中,然后%al寄存器中的数据与内存中的mutex数据交换,本质就是共享<->私有的过程,将唯一锁变成私有的。

需要注意的是,在汇编中exchan是一条汇编,代表这是原子性的,也就保证了锁的安全性。
在这里插入图片描述
这是unlock的伪代码,将1存入到内存中的mutex中。

#include <iostream>
#include <unistd.h>
#include <cstdio>
#include <pthread.h>
#include <string>
#define TNUM 5int ticket = 10000;pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;void *grab(void *args)
{const std::string name = (char *)args;while (1){pthread_mutex_lock(&mutex);// --- 临界区if (ticket > 0){usleep(1000);printf("%s : sells ticket:%d\n", name.c_str(), ticket);ticket--;// --- 临界区pthread_mutex_unlock(&mutex);}else{pthread_mutex_unlock(&mutex);break;}usleep(1000);//抢完票的后续动作}return nullptr;
}int main()
{pthread_t tid[TNUM];for (int i = 0; i < TNUM; i++){char name[64];snprintf(name, sizeof name, "new thread %d", i + 1);pthread_create(tid + i, nullptr, grab, (void *)name);usleep(10000);}for (int i = 0; i < TNUM; i++){pthread_join(tid[i], nullptr);}return 0;
}

在这里插入图片描述

加入了互斥锁之后,结果就没有问题了。


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

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

相关文章

Python模块-基础知识

Python模块-基础知识 1.模块分类&#xff1a; &#xff08;1&#xff09;自定义模块&#xff1a; 如果你自己写一个py文件&#xff0c;在文件内写入一堆函数&#xff0c;则它被称为自定义模块&#xff0c;即使用python编写的.py文件 &#xff08;2&#xff09;第三方模块&…

面向对象(C# )

面向对象&#xff08;C# &#xff09; 文章目录 面向对象&#xff08;C# &#xff09;ref 和 out传值调用和引用调用ref 和 out 的使用ref 和 out 的区别 结构体垃圾回收GC封装成员属性索引器静态成员静态类静态构造函数拓展方法运算符重载内部类和分布类 继承里氏替换继承中的…

BM23 二叉树的前序遍历

public class Solution {/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方法规定的值即可** * param root TreeNode类 * return int整型一维数组*/public void preorder(List<Integer> list,TreeNode root){if(root null)return;l…

C语言刷题1

和黛玉学编程呀 这期就是普普通通题目和答案啦&#xff0c;大都也比较基础&#xff0c;适合初学者&#xff0c;下期我们就更单链表啦 求Snaaaaaaaaaaaaaaa的前5项之和&#xff0c;其中a是一个数字&#xff0c; 例如&#xff1a;222222222222222 int main() {int a 0;int n …

力扣---完全平方数

思路&#xff1a; 还是比较好想的&#xff0c;g[i]定义为和为 i 的完全平方数的最少数量。那么递推关系式是g[i]min(g[i-1],g[i-4],g[i-9],...)1&#xff0c;数组初始化是g[0]0,g[1]1。注意这里要对g[0]初始化&#xff0c;&#xff08;举个例子&#xff09;因为在遍历到g[4]时&…

如何快速搭建一个完整的vue2+element-ui的项目-二

技术细节-继续配置 提示&#xff1a;你以为这样就完了吗,其实还有很多东西需要我们自己手写的 例如&#xff1a; element-ui的配置样式重置配置src使用的配置elinst配置axios异步请求的二次封转配置语言国际化配置(这个看需求,我这里就不用配置了)vuex的配置mixins的配置开发环…

淘宝店铺如何从1688一键铺货?官方授权API接口,可满足多样化上货需求

那么新手卖家如何将1688的源头厂货一键铺货到淘宝店铺呢&#xff1f;下面我教大家几招&#xff1a; 1、通过淘宝复制一键复制上货 淘宝API接口采集 taobao.item_get 公共参数 名称类型必须描述keyString是调用key&#xff08;必须以GET方式拼接在URL中&#xff09;secretStr…

未来已来?国内10家AI大模型盘点(附体验网址)

名人说&#xff1a;莫道桑榆晚&#xff0c;为霞尚满天。——刘禹锡&#xff08;刘梦得&#xff0c;诗豪&#xff09; 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 1、阿里云——通义千问2、科大讯飞——星火大模…

Windows 11 鼠标右键可选择 cmd 命令行选项

** Windows 11 鼠标右键可选择 cmd 命令行选项 ** 在文件夹内打开命令行&#xff0c;只能使用 Windows 自带的 PowerShell &#xff0c; 作为一个 cmd 重度使用用户来说很是折磨&#xff0c;需要打开 cmd 然后切换盘符再 cd 。。。 现在咱们自己创建一个可以打开 cmd 的方法…

Linux hook系统调用使你文件无法删除

文章目录 前言一、什么是hook技术二、Linux hook种类三、系统调用表hook3.1 查看删除文件用到系统调用3.2 获取系统调用函数3.3 编写hook函数3.4 替换hook函数3.5 测试 参考资料 前言 hook技术在Linux系统安全领域有着广泛的应用&#xff0c;例如通过hook技术可以劫持删除文件…

Unity Live Capture 中实现面部捕捉同步模型动画

Unity Face Capture 是一个强大的工具&#xff0c;可以帮助你快速轻松地将真实人脸表情捕捉到数字模型中。在本文中&#xff0c;我们将介绍如何在 Unity Face Capture 中实现面部捕捉同步模型动画。 安装 |实时捕获 |4.0.0 (unity3d.com) 安装软件插件 安装 Live Capture 软件…

C++利用开散列哈希表封装unordered_set,unordered_map

C利用开散列哈希表封装unordered_set,unordered_map 一.前言1.开散列的哈希表完整代码 二.模板参数1.HashNode的改造2.封装unordered_set和unordered_map的第一步1.unordered_set2.unordered_map 3.HashTable 三.string的哈希函数的模板特化四.迭代器类1.operator运算符重载1.动…

【Node.js从基础到高级运用】十三、NodeJS中间件高级应用

在现代web开发中&#xff0c;Node.js因其高效和灵活性而备受青睐。其中&#xff0c;中间件的概念是构建高效Node.js应用的关键。在这篇博客文章中&#xff0c;我们将深入探讨Node.js中间件的高级应用&#xff0c;包括创建自定义中间件、使用第三方中间件等。我们将从基础讲起&a…

旅游小程序在旅游营销中的作用及其优势

一、引言部分 随着科技的发展&#xff0c;移动互联网已经成为我们日常生活的一部分。对于旅游业来说&#xff0c;这也意味着新的机遇和挑战。其中&#xff0c;旅游小程序的出现为旅游业带来了全新的营销方式。本文将深入探讨旅游小程序在旅游营销中的作用以及其具体优势。 二、…

day12-SpringBootWeb 登录认证

一、登录功能 Slf4j RestController public class LoginController {Autowiredprivate EmpService empService;PostMapping("/login")public Result login(RequestBody Emp emp){log.info("员工登录: {}", emp);Emp e empService.login(emp);//登录失败, …

Java小项目--满汉楼

Java小项目–满汉楼 项目需求 项目实现 1.实现对工具包的编写 先创建libs包完成对jar包的拷贝和添加入库 德鲁伊工具包 package com.wantian.mhl.utils;import com.alibaba.druid.pool.DruidDataSourceFactory;import javax.sql.DataSource; import java.io.FileInputStream…

C# 设置AutoScroll为true没效果的原因分析和解决办法

C#中添加tabControl 分页&#xff0c;将autoscroll设置为true发现缩小窗口没有滚动条效果。该问题出现后&#xff0c;检索发现也有很多人询问了该问题&#xff0c;但是都没有给出解决方案。 原因是内部button的属性Anchor设置为top、left、right、bottom导致的缩小界面窗口也没…

QT_day2:2024/3/21

作业1&#xff1a;使用QT完成一个登录界面 要求&#xff1a; 1. 需要使用Ui界面文件进行界面设计 2. ui界面上的组件相关设置&#xff0c;通过代码实现 3. 需要添加适当的动图 源代码&#xff1a; #include "widget.h" #include "ui_widget.h"Widget…

数据结构:链式二叉树

对于二叉树而言,如果不是完全二叉树,就不再适合用数组存储了 在任意二叉树中&#xff0c;度为0的节点都比度为2的节点多1个&#xff0c;即 n0 n2 1 二叉树结构 typedef struct BinTreeNode {int val;struct BinTreeNode* left;struct BinTreeNode* right; }BTNode; 二叉树…

MySQL高级学习笔记

1、MySQL架构组成 1.1 高级MySQL介绍 什么是DBA&#xff1f; 数据库管理员&#xff0c;英文是Database Administrator&#xff0c;简称DBA&#xff1b; 百度百科介绍 数据库管理员&#xff08;简称DBA&#xff09;&#xff0c;是从事管理和维护数据库管理系统&#xff08;D…