【线程的互斥】

线程的互斥

    • 临界区资源
    • 多个线程的运行
    • 多个线程对同一资源的竞争
    • 原子性
    • 保持线程之间地互斥
      • 互斥量(锁的原理)为什么是原子的
    • 正确使用锁

临界区资源

进程创建线程,是共享内存的,可以对共享的资源有很方便的操作,当一些共享资源可以被多个线程进行访问操作,该共享的资源被称之为临界资源。比如多个线程如果都有对全局的静态变量进行访问或操作,则该变量就是共享资源。每个执行流的代码访问临界资源就叫做临界区。
在这里插入图片描述

多个线程的运行

多个线程在执行的时候,调度哪一个执行流是程序员不可知道的,是由操作系统OS来进行调度的,多个线程可能会有交叉的现象,这种现象是不允许的,比如订票的系统,如果两个线程出现了交叉的现象,可能就会造成最后一张票被两个线程进行获取,两个线程在竞争。有些线程的执行是需要其他的线程执行完之后才能被调度,线程A的资源可能要等到线程B运行完才会有,所以线程会有先后顺序。

多个线程对同一资源的竞争

在这里插入图片描述

1step:当票数为1的时候,线程A进行抢票,线程A先对票数进行检查,如果大于1,则进行先一步购票,对票数自减。线程A已经判断出票数剩余1,进行抢票,但系统发现线程A分配的运行时间已经到了,但线程A还未进行购票进行票数自减,然后暂时退出自己的执行流,但线程A会把已经处理的数据(CPU寄存器的数据,判断票数大于1)进行保存(保存在自己进程的上下文),在下次再次被调度时继续使用。
2step:此时,线程B开始被调度进行抢票,假设线程B的时间片足够长,线程B开始执行,也对票数进行判断,票数为1,因为线程A还没对票数进行自减时间片就到了,所以在系统的内存当中,票数依旧为1,然后线程B购票,对票数进行了自减,最后票数为0了,然后再把0的票数拷贝到系统内存,最后票数为0了。此时线程B结束。
3step:过了极短的时间,线程A又有了时间进行调度,因为线程A在之前调度时已经逻辑运算判断票数为1,并把数据保存在自己的进程上下文当中,再次调度时,线程A就会执行下一步购票的任务,首先会在系统的内存当中拷贝剩余的票数到CPU,然后进行自减,因为票数已经被线程B已经自减为0了,所以线程A会对票数0进行自减,最后得到-1的值,然后再把-1拷贝会系统内存当中。会发现剩余票数为-1,导致了数据不正确。对与上述的票资源,本质上是临界资源。是多线程程序中的共享变量。引发数据不准确的原因,又是因为线程时间片结束时,但执行流还未执行完,导致有些数据保存到进程独立的上下文最后因为这些行为不是原子的所导致。这种行为就是线程不是安全的。

原子性

在这里插入图片描述

票数的操作在C语言的情况下是不原子的,经过编译后会有三条汇编语句,虽然每条汇编语句是原子的。两条C语句经过汇编会生成5条汇编语句,每执行到一条汇编时,线程都有可能会被操作系统切换,一旦还没执行完全部任务,就会把这些寄存器的数据保存到自己的独立上下文。即使每条汇编是原子的,但多条汇编不一定是原子的,所以C语句不是原子的,每个线程调用时,不能保证在执行完整的过程中,保证该资源一直时一个线程独享的。

保持线程之间地互斥

为了保证共享资源得安全性,临界资源只能由一个执行流访问并进行操作,需要使用一些手段让临界资源只有一个执行流,这种就是互斥。可以通过添加互斥量(锁)这个资源来对多个线程对临界资源进行单独访问操作(加锁)。当执行流已经完成了临界资源得访问和临界区得代码执行完毕,就可以释这个互斥量(解锁)让其他得线程可以访问临界资源和申请锁这个资源继续单独得访问临界资源(锁是原子的)。

int cnt = 100;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;// 申请锁资源并初始化,这个锁是全局的
///
while(true)
{
	pthread_mutex_lock(&mutex);//加锁
	if (cnt > 0)
	    cnt--;
	pthread_mutex_unlock(&mutex);//解锁
}

如果一个线程成功申请了锁这个变量,在解锁和加锁这段临界区的临界资源,其他线程是无法访问到的,即使申请成功锁得线程随时被OS切换不调度,必须要等待该线程把锁资源释放掉了(等待过程就是阻塞得过程),其他线程才有机会申请到锁资源并访问临界资源(锁本身也得原子的)。

互斥量(锁的原理)为什么是原子的

互斥量为了保证自身是原子性的,为了实现这一目的,大多数体系结构提供了swap或exchange指令,这些指令用于交换两个操作数(通常是寄存器%al和内存单元mutex,寄存器的数据为线程独有的)的值。由于这些指令的执行是原子的,因此它们可以在多线程环境中安全地用于加锁和解锁操作。当%al为1和内存mutex为0时表明成功获得了锁。当线程完成对共享资源的访问并准备释放锁时,它只需将内存中的锁变量值重置为0即可。这通常可以通过简单的写入指令(而非swap或exchange指令)完成,因为此时没有其他线程会尝试获取锁。最后,多个线程达到了互斥的目的,此时,线程才是安全的,之前不加锁的时候是线程不安全的。
在这里插入图片描述

正确使用锁

对于临界资源,多个线程访问,进行申请锁,则每个线程需要申请的时同一个锁才有意义。如果操作不当,会产生死锁问题。
产生死锁的必要条件

  • 互斥条件:一个资源每次只能被一个执行流使用,因为要使用了锁。
  • 请求与保持条件:一个执行流因请求资源而阻塞时,对已获得的资源保持不放,一直独占该锁资源。
  • 不剥夺条件:一个执行流已获得的资源,在末使用完之前,不能强行剥夺。
  • 循环等待条件:若干执行流之间形成一种头尾相接的循环等待资源的关系。
    破坏死锁的四个必要条件
  • 避免死锁。
  • 加锁顺序一致。
  • 避免锁未释放的场景。
  • 资源一次性分配。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【找出第 K 大的异或坐标值】python

4层循环暴力超时 class Solution:def kthLargestValue(self, matrix: List[List[int]], k: int) -> int:nums[]for a in range(len(matrix)):for b in range(len(matrix[0])):num0for i in range(a1):for j in range(b1):num^matrix[i][j]nums.append(num)nums.sort()retu…

Golang实现文件复制

方法:三种 package zdpgo_fileimport ("errors""io""os" )// CopyFile 使用io.Copy进行文件的复制,同时也会复制文件的所有权限 // param src 复制文件 // param des 目标文件 // return error 错误信息 func CopyFile(s…

【QGIS入门实战精品教程】10.7: 基于DEM的地形因子分析(坡度、坡向、粗糙度、山体阴影、耐用指数)

文章目录 一、加载dem二、山体阴影三、坡度四、坡向五、地形耐用指数六、地形位置指数七、地表粗糙度一、加载dem 二、山体阴影 方法一:符号系统 利用符号系统中的山体阴影,渲染出阴影效果。 方法二:山体阴影工具 该算法计算输入中的数字化地形模型的山体阴影。根据太阳的位…

2024年教你怎么将学浪视频保存到本地

你是否曾为无法将学浪视频保存到本地而烦恼?现在,我们将在2024年教给你如何解决这个问题!只需简单几步操作,即可轻松将学浪视频保存到您的本地设备,随时随地想看就看! 我已经将下载学浪的工具打包好了&…

OSPF网络类型实验2

对R4 对R5,找R1注册 对R1宣告环回,再宣告一下tunnel接口 本实验不考虑区域划分 现在已经全部宣告完成 对R1,2,3改接口 broadcast工作方式hello时间10s,然后进行dr选举,由于2,3之间没有伪广播 …

滑不动窗口的秘密—— “滑动窗口“算法 (Java版)

本篇会加入个人的所谓鱼式疯言 ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. 🤭🤭🤭可能说的不是那么严谨.但小编初心是能让更多人能接…

淘工厂订单导出自动化工具

目录 下载安装与运行 主要目的 其他工具的弊端 本工具的优势 视频演示 下载新版后的注意事项 支持的导出项 什么叫一单多拍 常见问题 如何实现快捷登录 导出卡住时如何操作 如何精确导出 下载安装与运行 下载、安装与运行 语雀 主要目的 导出订单信息&#xf…

兴业证券 | 哪些行业在提价?

一方面, 部分行业年初以来PPI价格整体上涨,4月进一步提价;另一方面,部分行业年初以来PPI价格整体下跌或者涨幅不高,但4月开始出现边际提升。 前言:年初以来,“提价”是一条重要的投资线索。我们…

秋招突击——算法打卡——5/25、5/26——寻找两个正序数组的中位数

题目描述 自我尝试 首先,就是两个有序的数组进行遍历,遍历到一半即可。然后求出均值,下述是我的代码。但这明显是有问题的,具体错误的代码如下。计算复杂度太高了,O(n),所以会超时&…

Linux基础(六):Linux 系统上 C 程序的编译与调试

本篇博客详细分析,Linux平台上C程序的编译过程与调试方法,这也是我们后续程序开发的基础。 目录 一、第一个hello world程序 1.1 创建.c文件 1.2 编译链接 运行可执行程序 二、编译链接过程 2.1 预编译阶段 2.2 编译阶段 2.3 汇编阶段 2.4 链…

【Linux】常见命令:fping的介绍和用法举例

一、fping命令的安装 在终端中输入如下命令(Ubuntu系统使用apt install,CentOS系统使用yum install) sudo apt install fping安装效果(截图): 二、fping命令的用法和选项 fping命令用于检测主机是否存在…

2024最新 Jenkins + Docker实战教程(一) - Jenkins介绍及安装

😄 19年之后由于某些原因断更了三年,23年重新扬帆起航,推出更多优质博文,希望大家多多支持~ 🌷 古之立大事者,不惟有超世之才,亦必有坚忍不拔之志 🎐 个人CSND主页——Mi…

DiffMap:首个利用LDM来增强高精地图构建的网络

论文标题: DiffMap: Enhancing Map Segmentation with Map Prior Using Diffusion Model 论文作者: Peijin Jia, Tuopu Wen, Ziang Luo, Mengmeng Yang, Kun Jiang, Zhiquan Lei, Xuewei Tang, Ziyuan Liu, Le Cui, Kehua Sheng, Bo Zhang, Diange Ya…

大数据工具之HIVE-参数调优,调度乱码(二)

一、调度乱码 在利用HUE工具,搭建WORKFLOW流程的过程中,如果直接执行hivesql数据正常,不会出现乱码现象,如果利用WORKFLOW搭建的流程,进行数据的拉取,会出现数据中文乱码现象,这些乱码主要是由于select 中的硬编码中文导致出现的现象 具体现象如下: select case when …

auto关键字(C++11)

auto关键字(C11) 文章目录 auto关键字(C11)前言一、auto使用规则二、auto不适用的场景三、auto推荐适用的场景总结 前言 在C11中,auto关键字能够自动推导出变量的实际类型,可以帮助我们写出更加简洁、现代…

java8新特性——函数式编程详解

目录 一 概述1.1 背景1.2 函数式编程的意义1.3 函数式编程的发展 Lambda表达式1.1 介绍1.2 使用Lambda的好处1.3 Lambda方法1.3.1 Lambda表达式结构1.3.2 Lambda表达式的特征 1.4 Lambda的使用1.4.1 定义函数式接口1.4.2 Lambda表达式实现函数式接口1.4.3 简化Lambda表达式1.4.…

k8s devops实战教程+生产实践+可就业

k8s devops实战教程 简介教程涉及到内容教程获取学习教程后的收货助学群 简介 越来越多的企业应用云原生化,催生很多应用的部署方式也发生了很多变化。 从物理机部署应用过度到虚机部署应用再到应用容器化,从单应用再到服务拆分为微服务,靠人…

【手把手搓组件库】从零开始实现Element Plus--组件开发

从零开始实现Element Plus--组件开发 nvmnvm的作用:nvm的使用方法 需求分析提示词Kimi 生成产品需求文档kimi 生成测试用例 初始化 vitest完善 Button 组件1、定义 types.ts2、Button.vue 引入 types.ts3、添加Button样式点击事件 添加节流添加 Icon 集成 StoryBook…

红黑树封装map和set

红黑树源代码 我们将由下列的KV模型红黑树来模拟封装STL库中的map和set 注意&#xff1a;为了实现封装map和set&#xff0c;我们需要对下列源码进行优化。 #pragma once #include<iostream> using namespace std; //枚举类型的颜色分类 enum Colour {RED,BLACK };//定…

Collection

Collection是单列集合的祖宗接口&#xff0c;它的功能是全部单列集合都可以继承使用。 1.添加元素 public class test {public static void main(String [] args) {Collection<String> colnew ArrayList<>();col.add("aaa");col.add("bbb");…