Linux学习——锁

news2024/11/16 11:58:08

目录

​编辑

一,锁的概念

二,锁的操作

1,锁类型 pthread_mutex_t

2,初始化锁

3,上锁

4,解锁

5,销毁锁

三,线程安全问题演示

 四,锁的原理

五,死锁

1,死锁的概念

2,死锁的必要条件

3,一个线程自己也能造成死锁


 

一,锁的概念

在多线程当中。当多个线程并发的(在一段时间内一起访问一个公共资源)访问一个公共资源时就会导致数据不一致问题。所以为了避免这个问题(线程安全问题),所以要引入锁的概念,让这个公共资源在一段时间内变成一个临界资源(一段时间内只能有一个线程访问)。锁的作用便是将一个公共资源变成一个临界资源。

二,锁的操作

1,锁类型 pthread_mutex_t

pthread_mutex_t 是一个结构体,能够定义一个锁变量。

#endif
  } __data;
  char __size[__SIZEOF_PTHREAD_MUTEX_T];
  long int __align;
} pthread_mutex_t;

定义锁:

 pthread_mutex_t lock;

2,初始化锁

 int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);。有两个参数,其中第一个是锁变量的地址,第二个是要初始化成为的值,一般是nullptr.

   pthread_mutex_init(&lock, nullptr);

3,上锁

 int pthread_mutex_lock(pthread_mutex_t *mutex),能给代码上锁,上锁与解锁之间的这段代码叫做临界区代码(这段代码只允许单线程访问)

 pthread_mutex_lock(&lock);

4,解锁

int pthread_mutex_unlock(pthread_mutex_t *mutex),这个函数能给代码解锁让代码能够被其它的线程访问。

  pthread_mutex_unlock(&lock);

5,销毁锁

  int pthread_mutex_destroy(pthread_mutex_t *mutex),这个函数可以把锁变量给摧毁掉。

  pthread_mutex_destroy(&lock);
    pthread_mutex_t lock;//定义锁

    pthread_mutex_init(&lock, nullptr);//初始化锁

    pthread_mutex_lock(&lock);//上锁

    pthread_mutex_unlock(&lock);//解锁

    pthread_mutex_destroy(&lock);//销毁锁

三,线程安全问题演示

现在要演示的便是一个没有加锁的抢票系统,该例子的情形如下:

1,创建四个线程进行前票操作。

2,票为公共资源,抢票直到0为止。

代码如下:

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

int tickets = 1000;//票的数量

void*Get(void* args)
{
    uint64_t i =  (uint64_t)args;
    string name = "pthread_" + to_string(i);

    while(true)
    {
       if(tickets>0)//票还有时才抢
       {
           cout << name << " get: " << tickets << endl;
           tickets--;
       }
       else //没了就走
       {
           break;
       }

       usleep(1000);
    }
}

int main()
{
    vector<pthread_t> wait;
    for (uint64_t i = 1; i <= 4; i++)
    {
        pthread_t td;
        pthread_create(&td, nullptr, Get, (void *)i);
        wait.push_back(td);

        usleep(1000);
    }

    for(auto e:wait)
    {
        pthread_join(e,nullptr);
    }

    cout << "main quit....." << endl;

    return 0;
}

 结果如下:

可以看到这里会出现不同的线程抢到同一张票的情况,这个时候便是出现了线程安全的问题。 

解决方式:加入锁

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

int tickets = 1000;//票的数量

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;//定义锁并利用宏来初始化锁

void*Get(void* args)
{
    uint64_t i =  (uint64_t)args;
    string name = "pthread_" + to_string(i);

    while(true)
    {
        pthread_mutex_lock(&lock);//加锁
        if (tickets > 0) // 票还有时才抢
        {
            cout << name << " get: " << tickets << endl;
            tickets--;
       }
       else //没了就走
       {
           pthread_mutex_unlock(&lock);//解锁
           break;
       }
       pthread_mutex_unlock(&lock);//解锁

       usleep(1000);
    }
}

int main()
{
    vector<pthread_t> wait;
    for (uint64_t i = 1; i <= 4; i++)
    {
        pthread_t td;
        pthread_create(&td, nullptr, Get, (void *)i);
        wait.push_back(td);

        usleep(1000);
    }

    for(auto e:wait)
    {
        pthread_join(e,nullptr);
    }

    pthread_mutex_destroy(&lock);//销毁锁
    cout << "main quit....." << endl;

    return 0;
}

结果如下:

 

可以看到加入锁以后这个抢票的代码的结果便变成安全的了。 

 四,锁的原理

锁又叫做互斥量,当引入这个互斥量以后便可以让一段代码在一段时间内只能被一个线程访问。这是如何做到的呢?先来看一段锁的伪代码:

锁其实就是一个公共资源,这段汇编语句的图示如下:

第一步,将寄存器%al上的数据改为0,mutex(锁)内的值默认是1

第二步,交换%al与mutex上的值。这个时候%al上面的值是1并且%al上的值是线程1上的数据上下文。所以线程1保留了一个值1。

 

第三步,线程2切换进来,%al上的值被改为0。此时的线程2的值只能是0不能得到锁,所以会阻塞等待。

 

只有在线程1进行解锁操作时才能将锁归还,紧接着线程2才能得到锁进而访问公共资源

 将1换给mutex完成解锁让下一个线程访问这个资源。

所以锁其实就是设置一个变量1在多个线程中流动,一次只能由一个线程得到这个1,只有得到这个1的线程才能访问被锁住的资源。 

五,死锁

1,死锁的概念

      死锁出现的场景是两个线程在拥有各自的锁的情况下又去申请对方的锁,但是各自又不想释放自己拥有的锁,所以两个线程就会阻塞住不往下执行代码造成死锁。这就如同两个小孩各自有五毛钱,但是一根棒棒糖要一块钱才能买到,所以只有这两个小孩合作才能买到糖,但是各自又不想把自己的五毛钱给对方,所以就会导致大家都吃不到糖。

2,死锁的必要条件

###死锁四个必要条件
互斥条件:一个资源每次只能被一个执行流使用
请求与保持条件:一个执行流因请求资源而阻塞时,对已获得的资源保持不放
不剥夺条件:一个执行流已获得的资源,在末使用完之前,不能强行剥夺
循环等待条件:若干执行流之间形成一种头尾相接的循环等待资源的关系

3,一个线程自己也能造成死锁

一个线程多次申请锁,但是释放的次数与申请的次数不等,这样也会导致死锁问题。所以死锁问题一般都是程序员自己写的bug。

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

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

相关文章

每日OJ题_牛客HJ73 计算日期到天数转换(IO型OJ)

目录 牛客HJ73 计算日期到天数转换 解析代码 牛客HJ73 计算日期到天数转换 计算日期到天数转换_牛客题霸_牛客网 解析代码 #include <iostream> using namespace std; int main() {int year 0, month 0, day 0, sum 0;cin >> year >> month >>…

【SpringBoot框架篇】36.整合Tess4J搭建提供图片文字识别的Web服务

文章目录 简介文件下载引入依赖main函数中使用基于Springboot搭建OCR Web服务配置traineddata路径枚举用到的语种类型定义接口响应的json数据格式封装OCR服务引擎编写web提供服务的接口启动服务并且测试html demo扩展 项目配套代码 简介 Tess4J是一个基于Tesseract OCR引擎的J…

[Java安全入门]三.URLDNS链

一.前言 在初步学习java的序列化和反序列化之后&#xff0c;这里学习java反序列化漏洞的一个利用链&#xff0c;也是比较基础的一条链。 由于URLDNS不需要依赖第三方的包&#xff0c;同时不限制jdk的版本&#xff0c;所以通常用于检测反序列化的点。 二.代码展开分析 构造链 …

Learn OpenGL 03 着色器

GLSL 着色器的开头总是要声明版本&#xff0c;接着是输入和输出变量、uniform和main函数。每个着色器的入口点都是main函数&#xff0c;在这个函数中我们处理所有的输入变量&#xff0c;并将结果输出到输出变量中。 一个典型的着色器有下面的结构&#xff1a; #version vers…

[java入门到精通] 10 常用API , 正则表达式 , Collection集合

今日目标 BigInteger类BigDecimal类Arrays类包装类String类的常用方法正则表达式Collection集合 1 BigInteger类 1.1 概述 概述 : java.math.BigInteger类是一个引用数据类型 , 可以用于计算一些大的整数 , 当超出基本数据类型数据范围的整数运算时就可以使用BigInteger了。…

Arduino Uno使用Mind+实现图形化编程

文章目录&#xff1a; 一&#xff1a;软件下载安装 1.下载安装 1.1 开发软件 2.辅助软件 2.主控板 二&#xff1a;基础 1.LED 2.传感器 3.智能小车 三&#xff1a;学习资源 一&#xff1a;软件下载安装 1.下载安装 1.1 开发软件 Arduino IDE代码编程软件&#…

集合和数组的相关操作

目录 1.数组转集合(引用类型数组) 2.数组转集合(基础类型数组) 3.集合转数组 4.集合之间是否相交 5.获取两个集合的交集 6.集合转为字符串 1.数组转集合(引用类型数组) (1)Arrays.asList 示例&#xff1a; String[] colArr new String[6];colArr[0] "1";co…

Fastgithub

上Github太慢、打不开怎么办&#xff1f; 选择之一是Fastgithub工具&#xff0c;同时支持win, linux, mac。 1. 工作原理 从公共dns服务器拿到github的大量ip数据&#xff0c;检测哪些ip可用&#xff0c;哪些ip访问速度最佳&#xff0c;然后编写一个本地版的dns服务&#xff0…

小巧设备,大能量:探索口袋中的远程控制神器

在这个科技日新月异的时代&#xff0c;我们的生活被各种手机软件所包围。几乎每个人都有一个甚至多个手机&#xff0c;你是否也有遇到过需要远程操作自己某一台手机的场景呢&#xff1f;今天&#xff0c;我要向大家推荐一款神奇的手机远程操作神器&#xff0c;让你可以随时随地…

【EtherCAT实践篇】十、SSC工具使用说明

EtherCAT Slave Stack Code&#xff08;SSC&#xff09;是倍福提供的EtherCAT从站源代码生成工具&#xff0c;基于SSC工具&#xff0c;可以大大降低EtherCAT数据通讯程序及xml设计难度。 本操作参考SSC软件包中的EtherCAT Slave Design Quick Guide.pdf文档。 1、创建一个SSC工…

项目解决方案:视频监控接入和录像系统设计方案(下)

目 录 1.概述 2. 建设目标及需求 2.1建设总目标 2.2 需求描述 ​2.3 需求分析 3.设计依据与设计原则 3.1设计依据 3.2 设计原则 4.建设方案设计 4.1系统方案设计 4.2组网说明 5.产品介绍 5.1视频监控综合资源管理平台介绍 5.2视频录像服务器和存储 5.2.…

后勤管理系统|基于SSM 框架+vue+ Mysql+Java+B/S架构技术的后勤管理系统设计与实现(可运行源码+数据库+设计文档+部署说明+视频演示)

目录 文末获取源码 前台首页功能 员工注册、员工登录 个人中心 公寓信息 员工功能模块 员工积分管理 管理员登录 ​编辑管理员功能模块 个人信息 ​编辑员工管理 公寓户型管理 ​编辑公寓信息管理 系统结构设计 数据库设计 luwen参考 概述 源码获取 文末获取源…

bug总结(1)--变量取错

a c t i v i t y [ ′ t a g n a m e ′ ] 应为 activity[tag_name]应为 activity[′tagn​ame′]应为couponActivitList[0][‘name’] .隐藏的bug&#xff0c;在测试中竟然测不出来&#xff0c;而且上线了好久。为啥会出现这种低级错误呢&#xff1f;第一是写的时候不够仔细认…

C语言:基于单链表实现的泊车管理系统

一、需求 &#xff08;1&#xff09;管理员方账号登录&#xff1b; &#xff08;2&#xff09;车位管理显示&#xff1a;车位状态&#xff1b; &#xff08;3&#xff09;收费管理&#xff1a;小轿车 5元/小时&#xff0c;面包车6元/小时&#xff0c;大货车或客车7元/小时&a…

算法(6种思想、7种查找)、与数据结构(数组/链表/栈与队列/树)整理总结

算法 除了这里提到的算法思想和查找算法&#xff0c;算法还有别的类型&#xff1a; 排序算法&#xff1a; 对一组元素进行排序的算法。常见的排序算法包括冒泡排序、快速排序、归并排序等。 图算法&#xff1a; 解决图结构相关问题的算法&#xff0c;例如最短路径问题、最小…

Zookeeper详解

1.Zookeeper概述 1.Zookeeper概念 Zookeeper是 Apache Hadoop 项目下的一个子项目&#xff0c;是一个树形目录服务 Zookeeper 翻译过来就是动物园管理员&#xff0c;他是用来管 Hadoop&#xff08;大象&#xff09;、Hive(蜜蜂)、Pig(小猪)的管理员。简称zk Hadoop: 存储海…

【周总结周末日常】

周总结 完成任务开发并且与前端联调通过 完成已开发功能的冒烟测试 修复测试中出现的一些数据显示问题 2024/3/10 晴 温度适宜 这周天气比上周好多了&#xff0c;最起码见到好几次太阳 周六在世纪公园溜达一会儿&#xff0c;偶尔呼吸下大自然&#xff0c;挺棒的…

力扣hot100:152.乘积最大子数组(动态规划)

一个子数组问题&#xff0c;我们要使用线性dp&#xff0c;最好先考虑以i结尾&#xff0c;如果定义dp[i]为前i个数最大子数组乘积值 那么dp[i-1]就无法转移到dp[i]。因此我们先考虑dp[i]定义为以第i个数结尾的最大子数组乘积值。 53. 最大子数组和 最大子数组和是一个动态规划问…

重要通告 | 公司更名为“浙江实在智能科技有限公司”

更名公告 升级蜕变、砥砺前行 因业务快速发展和战略升级&#xff0c;经相关政府机构批准&#xff0c;自2024年3月1日起&#xff0c;原“杭州实在智能科技有限公司”正式更名为“浙江实在智能科技有限公司”。 更名后&#xff0c;公司统一社会信用代码不变&#xff0c;业务主体…

蓝桥杯单片机---第十二届省赛题目解析

文章目录 比赛题目一、代码相关定义、声明1.头文件声明2.变量声明 二、主要函数1.main函数2.按键扫描3.数码管显示4.电压模式1、2输出 & LED显示5.定时器中断6.消除85C显示 三、次要函数1.初始化函数Init2.按键函数Key3.LED函数Led4.数码管函数Seg5.iic函数中6.onewire函数…