Linux之多线程互斥

目录

线程互斥的概念

原子性 

线程互斥的引入

互斥锁

互斥锁的创建 

互斥锁的静态初始化

互斥锁的动态初始化

互斥锁的销毁

互斥锁加锁

互斥锁解锁 

互斥锁加锁和解锁的原理


上一期我们学习了线程控制,线程控制就是根据pthread线程库提供的线程接口对线程进行一系列的操作,本期我们学习的内容就是,当多个线程运行时,怎么保证线程合规,合理的运行。

线程互斥的概念

我们通过一个情景为大家引入线程互斥等相关概念。

在学生时代,中午放学铃声一响,同学们一窝蜂的跑向了学校的食堂去吃饭,食堂的窗口是有限的但是干饭的学生的数量却是很多的,所以势必会排成多列很长的队伍。队伍排头的人可以率先吃到饭,队伍末尾的人最后吃到饭,这也是同学们下课后快马加鞭的原因。按照计算机中的专业术语来说,队头和队尾决定吃饭的优先级。但是大家仔细想想,其实在排队打饭的过程中可能有这样几个不成文的规定。

1.在每个打饭的队伍中,只有队头的人可以打饭,且队头的人打饭的时候,其他人只能等着,不能去打饭。这保证了打饭的合规性

2.即使一个同学的饭量再大,打完饭之后就立即吃完,也不能接着打,打完饭之后,必须排在队列的末尾。这保证打饭的合理性

通过上述两个场景,引入了互斥和同步的概念。

互斥保证了多线程在访问临界资源时的合规性。即一个线程在访问临界资源的时候,其他线程不能不能访问。

同步保证了多线程在访问临界资源时的合理性。即一个线程在访问完临界资源之后,不能立刻去再次访问临界资源,必须被加入等待队列,等待其它线程访问完临界资源后再次访问

我们上文刚提到了临界资源,那么到底什么是临界资源呢?

我们称能被多个线程访问的资源为临界资源,我们称访问临界资源的代码为临界区。 最简单的理解就是我们创建的一个全局变量可以被多个线程访问。生活中的例子就是电影院的座位可以被多个顾客使用。  

原子性 

原子性:一件事,要做就做完,要么就不做。一般我们通过原子性来保证互斥。比如++和--操作都是原子性的。 

本期我们主要来研究线程互斥的相关内容。

线程互斥的引入

先看下述代码。

#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
#include<stdlib.h>

int ticket=100;

void* route(void* args)
{
    char* val =(char*)args;

    while(1)
    {
        if(ticket > 0)
        {
            sleep(1);
            printf("%s sells ticket: %d\n",val ,ticket);
            ticket--;
        }
        else{
            break;
        }
    }

}


int main()
{
    pthread_t t1,t2,t3,t4;

    pthread_create(&t1,NULL,route,"thead 1");
    pthread_create(&t2,NULL,route,"thead 2");
    pthread_create(&t3,NULL,route,"thead 3");
    pthread_create(&t4,NULL,route,"thead 4");

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

}

我们创建了4个线程,让这四个线程去抢我们定义的ticket票。注意,因为使用的是原生线程库的接口,所以在进行代码的编译时,必须以-pthread进行库的声明,不然会编译报错。运行结果如下。

通过运行结果截图我们惊奇的发现,票数竟然被减为了负数,这是为什么?到底处出了什么问题呢?

这是因为,ticket是一个临界资源,上述四个线程在访问临界资源的时候,因为没有互斥条件的约束。还剩最后一张票时,4,3,2,1号线程来了,发现票数都大于0,所以都进入了if语句,但是最终ticket的--操作,肯定是有先后顺序的,所以就会导致,4号线程一直到1号线程都会对票数进行--操作,最终将票数减为负数。

上述的代码,就是多线程在访问临界资源ticket的场景,不难发现,这种场景会出先很严重的问题,就是将票数减为了负数,那么该如何解决这种问题呢?这就需要用到我们本节课的知识,采用互斥的思想去解决。上文我们也提到了,互斥我们一般用原子性来实现,那么原子性我们一般采用什么实现呢?这就要引入我们本期的重点------互斥锁,我们通过互斥锁来实现线程访问临界资源的原子性,进一步实现多线程的互斥。

互斥锁

互斥锁是一种通过原子性操作保证多个线程访问临界资源时,达到互斥访问,保证线程安全的机制。

互斥锁的创建 

pthread_mutex_t mutex; 

互斥锁的静态初始化

pthread_mutex_t mutex= PTHREAD_MUTEX_INITIALIZER;

采用宏进行初始化。

互斥锁的动态初始化

pthred_mutex_init((pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr) ;

第一个参数mutex为要初始化的互斥锁量,第二个参数我们一般设置为NULL。

互斥锁的销毁

 pthread_mutex_destroy(&mutex);

注意:静态方法初始化的互斥锁不需要销毁

互斥锁加锁

pthread_mutex_lock(&mutex);

互斥锁解锁 

pthread_mutex_unlock(&mutex); 

代码如下。

#include<stdio.h>
#include<pthread.h>
#include<unistd.h>


int ticket=100;
pthread_mutex_t mutex;

void* route(void* args)
{
    char* val =(char*)args;
   
    while(1)
    {
           pthread_mutex_lock(&mutex);
        if(ticket > 0)
        {
            sleep(1);
            printf("%s sells ticket: %d\n",val ,ticket);
            ticket--;
            pthread_mutex_unlock(&mutex);
        }
        else{
              pthread_mutex_unlock(&mutex);
            break;
           
        }
 
    }
    
}


int main()
{
    pthread_t t1,t2,t3,t4;
    pthread_mutex_init(&mutex,NULL);

    pthread_create(&t1,NULL,route,"thead 1");
    pthread_create(&t2,NULL,route,"thead 2");
    pthread_create(&t3,NULL,route,"thead 3");
    pthread_create(&t4,NULL,route,"thead 4");

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

    pthread_mutex_destroy(&mutex);
}



运行结果如下。 

不难发现,此时ticket的数量已经恢复了正常,没有再出现负数。 那么整个互斥锁的原理是什么呢?接下来我们一起学习。

互斥锁加锁和解锁的原理

加锁和解锁的原理都是通过如下汇编代码来实现的。

 CPU中有很多的寄存器,多个线程在进行临界资源的访问时,会将自己的上下文数据保存在CPU特定的寄存器中,这些寄存器是多个线程共享的,但是寄存器中的数据,是每个线程独有的,互斥锁一般只有一把,当一个线程进行了加锁时,其它线程肯定是不能再次进行加锁的,这我们很容易想出来,但是计算机不知道,计算机时如何根据只有一把互斥锁的前提,从而使得一把锁一次只允许一个线程进行加锁呢?

互斥锁在多线程的情境下,只有一把互斥锁,所以,一般情况下,互斥锁的值我们初始化为1。在进行加锁时,先将线程cpu内部寄存器中的值设为0,然后与内存中mutex的值进行交换,然后进去if语句,如果寄存器中的值大于0,那么就证明申请锁成功并加锁,否则就挂起等待。如果此时第一个线程已经完成了加锁,那么此时寄存器中的值就为1,而内存中的mutex的值就为0,所以当下一个线程来再次申请锁时,即使当前线程的寄存器中的值与内存中的mutex的值发生了交换,因为之前第一个线程已经进行了加锁,并没有解锁,所以mutex的值还为0,所以当前线程的寄存器的值在交换前后并没有发生变化,所以只能挂起等待。在进行解锁的时候,会将mutex的值重新设置为1,然后后续线程再次进行lock中对应的汇编操作。

因为lock(加锁)和unlock(解锁)之间是访问临界资源的代码,所以加锁之后的线程很有可能在执行临界资源的代码过程中, 因为时间片到了被切换走,但是即使被切换走了,也是抱着锁被切走挂起,所以一直没有解锁,只有等到这个线程被再次唤醒,执行完临界区的代码,并解锁,其它线程才有加锁的机会,才有访问临界资源的机会。所以即要么一个线程没有加锁,要么加了锁之后执行完了临界区的代码并解锁,这样对其它的线程才是具有意义的。也就是通过互斥锁的这种加锁和解锁方式,实现了多个线程访问临界资源的互斥。

以上便是本期线程互斥的所有相关内容。

本期内容到此结束^_^

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

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

相关文章

Android4.4 在系统中添加自己的System Service

添加系统service时&#xff0c;源码限制只能添加以android开头的包名&#xff0c;如果不是android开头的&#xff0c;编译时会提示找不到对应的文件。 比如说在系统中添加一个包名为&#xff1a;tel.gateway.connservice的系统服务。 1.在framework/base目录下面创建如下路径&a…

芝法酱学习笔记(2.2)——sql性能优化2

一、前言 在上一节中&#xff0c;我们使用实验的方式&#xff0c;验证了销售单报表的一些sql性能优化的猜想。但实验结果出乎我们的意料&#xff0c;首先是时间查询使用char和datetime相比&#xff0c;char可能更快&#xff0c;使用bigint&#xff08;转为秒&#xff09;和cha…

安装Linux操作系统

确保虚拟机安装成功&#xff0c;接下来开始安装操作系统&#xff0c;通过虚拟光驱安装。 1. 点击图中的 CD/DVD &#xff0c;设置光盘文件&#xff0c;光盘文件下载地址&#xff1a; https://mirrors.tuna.tsinghua.edu.c n/centos-vault/8.5.2111/isos/x86_64/ 说明&#xf…

【网络安全产品大调研系列】1. 漏洞扫描

1. 为什么会出现漏扫技术&#xff1f; 每次黑客攻击事件进行追溯的时候&#xff0c;根据日志分析后&#xff0c;我们往往发现基本都是系统、Web、 弱口令、配置这四个方面中的其中一个出现的安全问题导致黑客可以轻松入侵的。 操作系统的版本滞后&#xff0c;没有更新补丁&am…

Java CountDownLatch 用法和源码解析

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;…

AFL-Fuzz 的使用

AFL-Fuzz 的使用 一、工具二、有源码测试三、无源码测试 一、工具 建议安装LLVM并使用afl-clang-fast或afl-clang-lto进行编译&#xff0c;这些工具提供了更现代和高效的插桩技术。您可以按照以下步骤安装LLVM和afl-clang-fast&#xff1a; sudo apt update sudo apt install…

Java项目--仿RabbitMQ的消息队列--网络通信协议设计

目录 一、引言 二、设计 三、代码 1.Request 2.Response 3.BasicArguments 4.BasicReturns 四、方法类 1.创建交换机 2.删除交换机 3.创建队列 4.删除队列 5.创建绑定 6.删除绑定 7.消息发布 8.消费消息 9.集中返回 五、实现Broker Server类 六、实现连…

MySQL通过binlog日志进行数据恢复

记录一次阿里云MySQL通过binlog日志进行数据回滚 问题描述由于阿里云远程mysql没有做安全策略 所以服务器被别人远程攻击把数据库给删除&#xff0c;通过查看binlog日志可以看到进行了drop操作&#xff0c;下面将演示通过binlog日志进行数据回滚操作。 1、查询是否开始binlog …

王佩丰24节Excel学习笔记——第十二讲:match + index

【以 Excel2010 系列学习&#xff0c;用 Office LTSC 专业增强版 2021 实践】 【本章小技巧】 vlookup与match&#xff0c;index 相结合使用match,index 结合&#xff0c;快速取得引用的值扩展功能&#xff0c;使用match/index函数&#xff0c;结合照相机工具获取照片 一、回顾…

《Time Ghost》的制作:使用 DOTS ECS 制作更为复杂的大型环境

*基于 Unity 6 引擎制作的 demo 《Time Ghost》 开始《Time Ghost》项目时的目标之一是提升在 Unity 中构建大型户外环境的构建标准。为了实现这一目标&#xff0c;我们要有处理更为复杂的场景的能力、有足够的工具支持&#xff0c;同时它对引擎的核心图形、光照、后处理、渲染…

【考前预习】4.计算机网络—网络层

往期推荐 【考前预习】3.计算机网络—数据链路层-CSDN博客 【考前预习】2.计算机网络—物理层-CSDN博客 【考前预习】1.计算机网络概述-CSDN博客 目录 1.网络层概述 2.网络层提供的两种服务 3.分类编址的IPV4 4.无分类编址的IPV4—CIDR 5.IPV4地址应用规划 5.1使用定长子…

解决pip下载慢

使用pip下载大量安装包&#xff0c;下载速度太慢了 1、问题现象 pip安装包速度太慢 2、解决方案 配置国内源 vi /root/.config/pip/pip.conf[global] timeout 6000 index-url https://mirrors.aliyun.com/pypi/simple/ trusted-host mirrors.aliyun.com

【Linux】Linux权限管理:文件与目录的全面指南

在Linux系统中&#xff0c;权限管理是确保数据安全的关键。本文将为大家介绍Linux文件与目录的权限管理&#xff0c;帮助你理解如何设置和管理访问权限。无论你是新手还是有经验的用户&#xff0c;这里都将提供实用的技巧和知识&#xff0c;助你更好地掌握Linux环境。让我们一起…

【模型压缩】原理及实例

在移动智能终端品类越发多样的时代&#xff0c;为了让模型可以顺利部署在算力和存储空间都受限的移动终端&#xff0c;对模型进行压缩尤为重要。模型压缩&#xff08;model compression&#xff09;可以降低神经网络参数量&#xff0c;减少延迟时间&#xff0c;从而实现提高神经…

Android Stduio 2024版本设置前进和后退按钮显示在主界面

Android Studio 2024&#xff08;Ladybug&#xff09;安装后发现前进和后退按钮不显示在主界面的工具栏&#xff0c;且以前在View中设置的办法无效&#xff1a; Android Studio 2024&#xff08;Ladybug&#xff09;的设置方式&#xff1a; File->Settings->Appearance&…

MySQL数据库——门诊管理系统数据库数据表

门诊系统数据库his 使用图形化工具或SQL语句在简明门诊管理系统数据库his中创建数据表&#xff0c;数据表结构见表2-3-9&#xff5e;表2-3-15所示。 表2-3-9 department&#xff08;科室信息表&#xff09; 字段名称 数据类型 长度 是否为空 说明 dep_ID int 否 科室…

Ubuntu上如何部署Nginx?

环境&#xff1a; Unbuntu 22.04 问题描述&#xff1a; Ubuntu上如何部署Nginx&#xff1f; 解决方案&#xff1a; 在Ubuntu上部署Nginx是一个相对简单的过程&#xff0c;以下是详细的步骤指南。我们将涵盖安装Nginx、启动服务、配置防火墙以及验证安装是否成功。 1. 更新…

【从零开始入门unity游戏开发之——C#篇08】逻辑运算符、位运算符

文章目录 一、逻辑运算符1、**&&&#xff08;逻辑与&#xff09;**语法&#xff1a;示例&#xff1a; 2、**||&#xff08;逻辑或&#xff09;**语法&#xff1a;示例&#xff1a; 3、**!&#xff08;逻辑非&#xff09;**语法&#xff1a;示例&#xff1a; 4、**^&…

【Android开发】安装Android Studio(2023.1.1)

下载安装包 Android Studio2023.1.1百度云盘下载&#xff0c;提取码&#xff1a;6666https://pan.baidu.com/s/1vNJezi7aDOP0poPADcBZZg?pwd6666 安装Android Studio 2023.1.1 双击下载好的安装包 弹出界面点击下一步 继续点击【Next】 更改安装路径后继续点击【Next】 点…

.net winform 实现CSS3.0 泼墨画效果

效果图 代码 private unsafe void BlendImages1(Bitmap img1, Bitmap img2) {// 确定两个图像的重叠区域Rectangle rect new Rectangle(0, 0,Math.Min(img1.Width, img2.Width),Math.Min(img1.Height, img2.Height));// 创建输出图像&#xff0c;尺寸为重叠区域大小Bitmap b…