Linux一学就会——线程互斥

news2024/12/23 9:45:17

Linux一学就会——线程互斥

Linux线程的互斥

进程线程间的互斥相关背景概念

临界资源:多线程执行流共享的资源就叫做临界资源
临界区:每个线程内部,访问临界自娱的代码,就叫做临界区
互斥:任何时刻,互斥保证有且只有一个执行流进入临界区,访问临界资源,通常对临界资源起保护作用
原子性(后面讨论如何实现):不会被任何调度机制打断的操作,该操作只有两态,要么完成,要么未完成

互斥量mutex

在这里插入图片描述

大部分情况,线程使用的数据都是局部变量,变量的地址空间在线程栈空间内,这种情况,变量归属单个线程,其他线程无法获得这种变量。
但有时候,很多变量都需要在线程间共享,这样的变量称为共享变量(全局变量、静态变量等),可以通过数据的共享,完成线程之间的交互。(放在已初始化数据段内)
多个线程并发的操作共享变量,会带来一些问题。

测试函数的书写

#include<iostream>
#include<unistd.h>
#include<thread>
#include<pthread.h>
using namespace std;

int tickets=10000;

void* threadrout(void* arg)
{
    char* tname=(char*)arg;
    while(1)
    {
        if(tickets>0)
        {
            usleep(1000);
            cout<<tname<<" : 抢到一张,还剩 : "<<--tickets<<endl;
        }
        else
        {
            break;
        }
    }
}

int main()
{
    pthread_t t1,t2,t3,t4;
    pthread_create(&t1,NULL,threadrout,(void*)"t1");
    pthread_create(&t2,NULL,threadrout,(void*)"t2");
    pthread_create(&t3,NULL,threadrout,(void*)"t3");
    pthread_create(&t4,NULL,threadrout,(void*)"t4");

    pthread_join(t1,NULL);
    pthread_join(t2,NULL);
    pthread_join(t3,NULL);
    pthread_join(t4,NULL);

    return 0;
}

makefile文件的书写

testThread:testThread.cc
	g++ -o $@ $^ -std=c++11 -lpthread

.PHONY clean:
clean:
	rm -f testThread

在这里插入图片描述
居然减到负数了呢!这要是用在生活当中,一家电影院多卖了三个人的票,总不能让这三个人集体站票吧?
如果你做过单片机的项目也会出现这种情况,但单片机用消抖来消除按键抖动的,可是一个程序一定需要效率,不可能消抖呀,所以要用别的方法。

为什么可能无法获得争取结果?

  • if 语句判断条件为真以后,代码可以并发的切换到其他线程
  • usleep这个模拟漫长业务的过程,在这个漫长的业务过程中,可能有很多个线程会进入该代码段
  • –ticket操作本身就不是一个原子操作(不是一条汇编指令完成的)
    在这里插入图片描述
    减减 操作并不是原子操作,而是对应三条汇编指令:
  • load:将共享变量ticket从内存加载到寄存器中
  • update: 更新寄存器里面的值,执行-1操作
  • store:将新值,从寄存器写回共享变量ticket的内存地址

要解决以上问题,需要做到三点:

  • 代码必须要有互斥行为:当代码进入临界区执行时,不允许其他线程进入该临界区。
  • 如果多个线程同时要求执行临界区的代码,并且临界区没有线程在执行,那么只能允许一个线程进入该临界区。
  • 如果线程不在临界区中执行,那么该线程不能阻止其他线程进入临界区。

要做到这三点,本质上就是需要一把锁。Linux上提供的这把锁叫互斥量。
在这里插入图片描述
互斥量的接口
初始化互斥量
初始化互斥量有两种方法:

  • 方法1,静态分配:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER
  • 方法2,动态分配:
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
						 const pthread_mutexattr_t *restrict attr);
参数:
	mutex:要初始化的互斥量
	attr:NULL

采用动态加锁代码

#include<iostream>
#include<unistd.h>
#include<thread>
#include<pthread.h>
using namespace std;

int tickets=10000;
pthread_mutex_t mx;

void* threadrout(void* arg)
{
    char* tname=(char*)arg;
    while(1)
    {
        pthread_mutex_lock(&mx);
        //while中包裹的都是临界区
        if(tickets>0)
        {
            usleep(1000);
            cout<<tname<<" : 抢到一张,还剩 : "<<--tickets<<endl;
            pthread_mutex_unlock(&mx);
        }
        else
        {
            pthread_mutex_unlock(&mx);
            break;

        }
    }
}

int main()
{
    pthread_mutex_init(&mx,NULL);
    pthread_t t1,t2,t3,t4;
    pthread_create(&t1,NULL,threadrout,(void*)"t1");
    pthread_create(&t2,NULL,threadrout,(void*)"t2");
    pthread_create(&t3,NULL,threadrout,(void*)"t3");
    pthread_create(&t4,NULL,threadrout,(void*)"t4");

    pthread_join(t1,NULL);
    pthread_join(t2,NULL);
    pthread_join(t3,NULL);
    pthread_join(t4,NULL);

    pthread_mutex_destroy(&mx);

    return 0;
}

在这里插入图片描述
速度一下就慢了好多。

但是有一个问题,就是一个线程会枪好久,一直不轮转。
如图:
在这里插入图片描述
稍微修改一下:

void* threadrout(void* arg)
{
    char* tname=(char*)arg;
    while(1)
    {
        pthread_mutex_lock(&mx);
        //while中包裹的都是临界区
        if(tickets>0)
        {
            usleep(1000);
            cout<<tname<<" : 抢到一张,还剩 : "<<--tickets<<endl;
            pthread_mutex_unlock(&mx);
        }
        else
        {
            pthread_mutex_unlock(&mx);
            break;

        }
        usleep(1000);//增加一个休息轮转的时间
    }
}

在这里插入图片描述
在这里插入图片描述

但是速度就又慢了一些。

互斥量实现原理探究

  • 经过上面的例子,大家已经意识到单纯的i++或者++i都不是原子的,有可能会有数据一致性问题
    在这里插入图片描述
    我们的加锁是只有一条汇编执行的,就是exchange指令
  • 为了实现互斥锁操作,大多数体系结构都提供了swap或exchange指令,该指令的作用是把寄存器和内存单元的数据相交换,由于只有一条指令,保证了原子性,即使是多处理器平台,访问内存的 总线周期也有先后,一个处理器上的交换指令执行时另一个处理器的交换指令只能等待总线周期。 现在我们把lock和unlock的伪代码改一下

在这里插入图片描述

可重入VS线程安全

概念

  • 线程安全:多个线程并发同一段代码时,不会出现不同的结果。常见对全局变量或者静态变量进行操作,并且没有锁保护的情况下,会出现该问题。
  • 重入:同一个函数被不同的执行流调用,当前一个流程还没有执行完,就有其他的执行流再次进入,我们称之为重入。一个函数在重入的情况下,运行结果不会出现任何不同或者任何问题,则该函数被称为可重入函数,否则,是不可重入函数。

常见的线程不安全的情况

  • 不保护共享变量的函数
  • 函数状态随着被调用,状态发生变化的函数
  • 返回指向静态变量指针的函数
  • 调用线程不安全函数的函数

常见的线程安全的情况

  • 每个线程对全局变量或者静态变量只有读取的权限,而没有写入的权限,一般来说这些线程是安全的
  • 类或者接口对于线程来说都是原子操作
  • 多个线程之间的切换不会导致该接口的执行结果存在二义性

常见不可重入的情况

调用了malloc/free函数,因为malloc函数是用全局链表来管理堆的
调用了标准I/O库函数,标准I/O库的很多实现都以不可重入的方式使用全局数据结构
可重入函数体内使用了静态的数据结构

常见可重入的情况

不使用全局变量或静态变量
不使用用malloc或者new开辟出的空间
不调用不可重入函数
不返回静态或全局数据,所有数据都有函数的调用者提供
使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据

可重入与线程安全联系

函数是可重入的,那就是线程安全的
函数是不可重入的,那就不能由多个线程使用,有可能引发线程安全问题
如果一个函数中有全局变量,那么这个函数既不是线程安全也不是可重入的。

可重入与线程安全区别

可重入函数是线程安全函数的一种
线程安全不一定是可重入的,而可重入函数则一定是线程安全的。
如果将对临界资源的访问加上锁,则这个函数是线程安全的,但如果这个重入函数若锁还未释放则会产生死锁,因此是不可重入的。

死锁问题我们下节课讲。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python3数据分析与挖掘建模(3)探索性数据分析

1. 概述 探索性数据分析&#xff08;Exploratory Data Analysis&#xff0c;EDA&#xff09;是一种数据分析的方法&#xff0c;用于探索和理解数据集的特征、关系和分布等。EDA旨在揭示数据中的模式、异常值、缺失值等信息&#xff0c;并为后续的分析和建模提供基础。以下是关…

20-01 走进微服务与Spring Cloud

Java架构师系列导航目录 认识SpringCloud——外带全家桶 Alibaba组件库 Nacos 中心化动态配置 持久化规则服务发现&#xff08;DNS RPC&#xff09;权重路由无缝SC K8s Sentinel&#xff08;流控组件&#xff09; 突发流量、削峰填谷、流量整形实时熔断实时监控大盘 S…

汽车和地铁的无人驾驶了解

01汽车无人驾驶技术 汽车相对地铁列车&#xff0c;控制设备的安装空间较为有限&#xff0c;不同车辆的个体差异较大&#xff0c;其无人驾驶技术的实现方案需要更简约&#xff0c;主流的方案通常是通过多种车载传感器&#xff08;如摄像头、激光雷达、毫米波雷达、北斗/GPS、惯性…

实时频谱-3.1实时频谱分析仪测量

RSA 测量类型 泰克RSA 可以在频域、时域、调制域和统计域中工作。 频域测量 基本频域测量是实时 RF 数字荧光显示(DPX)频谱显示测量、频谱显示测量和频谱图显示测量功能。 DPX 频谱 DPX 频谱测量对 RSA 发现其它分析仪漏掉的难检信号的能力至关重要。在所有泰克 RSA 中&am…

聚观早报|知乎发布搜索聚合开始内测;苹果7月关闭我的照片流服务

今日要闻&#xff1a;知乎发布“搜索聚合”即日开启内测&#xff1b;iOS版ChatGPT下载量突破50万次&#xff1b;苹果7月关闭“我的照片”流服务&#xff1b;首款国产介入手术机器人亮相&#xff1b;马斯克回应多年前嘲笑比亚迪 知乎发布“搜索聚合”即日开启内测 5 月 28 日消…

ChatGTP全景图 | 背景+技术篇

引言&#xff1a;人类以为的丰功伟绩&#xff0c;不过是开端的开端……我们在未来100年取得的技术进步&#xff0c;将远超我们从控制火种到发明车轮以来所取得的一切成就。——By Sam Altman 说明&#xff1a;ChatGPT发布后&#xff0c;我第一时间体验了它的对话、翻译、编程、…

Linux-0.11 kernel目录进程管理asm.s详解

Linux-0.11 kernel目录进程管理asm.s详解 模块简介 该模块和CPU异常处理相关&#xff0c;在代码结构上asm.s和traps.c强相关。 CPU探测到异常时&#xff0c;主要分为两种处理方式&#xff0c;一种是有错误码&#xff0c;另一种是没有错误码&#xff0c;对应的方法就是error_c…

Logisim 头歌 16位海明编码电路设计 图解及代码(计算机组成原理)

努力是为了不平庸~ 学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。 急的同学请直接点击目录跳到下方解答处&#xff01;&#xff01; 目录 图解&#xff1a; 代码题解&#xff08;免费&#xff09;&#xff1a; 实…

SpringCloudConfigServer配置刷新优化方案

前一文章《SpringCloudConfigServer配置中心使用与刷新详解》 介绍了Spring Cloud原生配置中心的部署方案&#xff0c;以及配置变更时的刷新方案。 通过该文可以看到&#xff1a; 第一种方案无法同时刷新单个服务的所有实例第二种方案依赖于消息中间件&#xff08;RabbitMQ或k…

自动驾驶汽车的安全技术特点

“安全第一”是自动驾驶的核心理念和价值观。 自动驾驶车辆的整体系统安全设计是一项复杂的系统工程&#xff0c; 涉及车载自动驾驶系统的核心算法策略设计、 硬件和软件冗余安全设计、远程云代驾技术、 全流程测试验证技术等&#xff0c; 并遵循功能安全&#xff08;ISO 2626…

《数据库应用系统实践》------ 酒店客房管理系统

系列文章 《数据库应用系统实践》------ 酒店客房管理系统 文章目录 系列文章一、需求分析1、系统背景2、 系统功能结构&#xff08;需包含功能结构框图和模块说明&#xff09;3&#xff0e;系统功能简介 二、概念模型设计1&#xff0e;基本要素&#xff08;符号介绍说明&…

品种小组2期—凯利公式在RFI策略中的运用

量化策略开发&#xff0c;高质量社群&#xff0c;交易思路分享等相关内容 大家好&#xff0c;今天我们来聊一聊松鼠2期V2版本的阶段内容——凯利公式在RFI择时框架上的运用。 松鼠品种小组2期第1版策略、讲解视频已完结&#xff0c;该期小组我们分享了全新“普适性、自适应”择…

0基础学习VR全景平台篇第32章:场景功能-嵌入视频

大家好&#xff0c;欢迎观看蛙色VR官方系列——后台使用课程&#xff01; 一、本功能将用在哪里&#xff1f; 嵌入功能可对VR全景作品嵌入【图片】【视频】【文字】【标尺】四种不同类型内容&#xff1b; 本次主要带来视频类型的介绍&#xff0c;通过嵌入视频功能&#xff0c;…

python+django+vue关爱无主狗流浪狗动物领养公益网站

很多的家庭都开始有养个小宠物的习惯&#xff0c;平时可以排解寂寥。随着近些年来大家养宠物的数量逐日剧增,人类对自己行为的规范与责任感渐渐的缺失,从而造成社会上的流浪动物也越来越多。流浪狗的伤人的事件则更多的出现在了报纸&#xff0c;电视&#xff0c;网络上。国家社…

git 项目演练:007

接下来进行项目演练&#xff0c;这是一个项目提交到git一个完整过程 1. 创建一个项目&#xff0c; 如下&#xff0c;我创建了一个“测试项目” 2. 将项目添加到git管理仓库&#xff0c;打开Git Bash&#xff0c; cd到“测试项目”中 3. 使用git init 命令将项目添加&#xff0c…

python基于pygame库实现弹幕效果(多行显示,速度不同,颜色不同,循环显示)

一、实现目标 使用python实现类似弹幕的显示效果,弹幕文本存储在txt文件中,弹幕多行显示,弹幕颜色不同有区别,弹幕的速度不一,弹幕要循环显示。 弹幕文本数据:danmu.txt 永远的神 哥哥,好帅啊 啊啊啊啊 太好看啦 666 爱了爱了 啦啦啦啦啦啦 牛逼!牛逼!牛逼! 厉害啊…

5G的遮羞布又被撕了,只是这次是韩国,5G为何变成如此模样?

日前韩国公平交易委员会发布声明&#xff0c;对韩国三大运营商重罚336亿韩元&#xff0c;原因是韩国夸大5G网速&#xff0c;实际网速不到理论速率的十分之一&#xff0c;可以说韩国撕下了5G的遮羞布。 韩国公平交易委员会指出运营商宣传5G的时候强调5G的理论速度可以达到10Gbps…

JavaScrip练习

HTMLJS计算器 <!DOCTYPE html> <html> <head><title>Calculator</title> </head> <body> <!-- 计算器显示 --> <input type"text" id"display" disabled> <br><!--计算器按钮onclick &am…

数据偏度介绍和处理方法

偏度&#xff08;skewness&#xff09;是用来衡量概率分布或数据集中不对称程度的统计量。它描述了数据分布的尾部&#xff08;tail&#xff09;在平均值的哪一侧更重或更长。偏度可以帮助我们了解数据的偏斜性质&#xff0c;即数据相对于平均值的分布情况。 有时&#xff0c;正…

自主品牌份额持续提升!福特CEO表态,中国车企才是竞争对手

2023年1-4月&#xff0c;中国市场自主品牌乘用车新车交付275.13万辆&#xff0c;份额占比48.63%&#xff0c;两项数字分别比上年同期增长4.68%&#xff0c;以及提升约3个百分点。其中&#xff0c;新能源汽车继续成为主要推动因素。 “中国电动汽车制造商是我们的主要竞争对手&a…