Linux学习——锁

目录

​编辑

一,锁的概念

二,锁的操作

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。

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

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

相关文章

《IAB视频广告标准:综合指南(2022)》之概述篇 - 我为什么要翻译介绍美国人工智能科技公司IAB 系列(2)

IAB平台&#xff0c;使命和功能 IAB成立于1996年&#xff0c;总部位于纽约市。 作为美国的人工智能科技巨头社会媒体和营销专业平台公司&#xff0c;互动广告局&#xff08;IAB- the Interactive Advertising Bureau&#xff09;自1996年成立以来&#xff0c;先后为700多家媒体…

每日一练:LeeCode-56、合并区间【数组+滑动窗口】

4.合并区间 LeeCode-56、合并区间 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需恰好覆盖输入中的所有区间 。 1 < intervals.le…

React-嵌套路由

1.概念 说明&#xff1a;在一级路由中又内嵌了其他路由&#xff0c;这种关系就叫做嵌套路由&#xff0c;嵌套至一级路由内的路由又称作二级路由。 2.实现步骤 说明&#xff1a;使用childen属性配置路由嵌套关系&#xff0c;使用<Outlet/>组件配置二级路由渲染的位置。…

重读 Java 设计模式: 解析单例模式,保证唯一实例的创建与应用

本周工作太忙了&#xff0c;变成了加班狗&#xff0c;下班回来也没时间写&#xff0c;只能利用周末时间写了&#x1f62d;。 好了&#xff0c;言归正传&#xff0c;本次我们先来介绍下设计模式中创建型模式-单例模式。 一、引言 单例模式是设计模式中最简单但又最常用的一种模…

OpenCV 绘制文字的介绍

1、前言 OpenCV 提供了用于绘制文字的putText()方法 使用这个方法不仅能够设置字体的样式、大小和颜色&#xff0c;而且能够使字体呈现斜体的效果&#xff0c;还能够控制文字的方向&#xff0c;进而使文字呈现垂直像的效果。putText()方法的语法格式如下 需要注意的是&#x…

新版ui周易测算网站H5源码/在线起名网站源码/运势测算网站系统源码,附带系统搭建教程

支持对接第三方支付 安装方法以linux为例 1、建议在服务器上面安装宝塔面板&#xff0c;以便操作&#xff0c;高逼格技术员可以忽略这步操作。 2、把安装包文件解压到根目录&#xff0c;同时建立数据库&#xff0c;把数据文件导入数据库 3、修改核心文件config/inc_config.…

java通过poi-tl生成word

我看公司之前做电子合同&#xff0c;使用TIBCO jaspersoft做的报表模板&#xff0c;如果是给自己公司开发或者给客户做项目&#xff0c;这个也没有什么&#xff0c;因为反正模板是固定的&#xff0c;一次性开发&#xff0c;不用担心后续的问题。即使后期有调整&#xff0c;改一…

023—pandas 扩展逗号爆炸分隔字符串数据

需求&#xff1a; 将 c1 按逗号拆分&#xff0c;爆炸为一行一行数据&#xff0c;然后将 c1 后边的有逗号的扩展成行&#xff0c;没逗号的只写在第一行。 思路&#xff1a; 先将 DataFrame 中有逗号的值分拆转为列表&#xff0c;接下来我们对 c1 进行爆炸&#xff0c;就得到了…

排序算法全景:从基础到高级的Java实现

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

(黑马出品_06)SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式

&#xff08;黑马出品_06&#xff09;SpringCloudRabbitMQDockerRedis搜索分布式 微服务技术ES搜索和数据分析 今日目标1. 查询文档1.1.DSL查询分类1.2.全文检索查询1.2.1.使用场景1.2.2.基本语法1.2.3.示例 1.3.精准查询1.3.1.term查询1.3.2.ran…

【机器学习】包裹式特征选择之基于模型的特征选择法

&#x1f388;个人主页&#xff1a;豌豆射手^ &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f917;收录专栏&#xff1a;机器学习 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共同学习、交流进…

解决Ubuntu 16.04/18.04 图形化界面异常、鼠标光标消失、鼠标变成叉叉等问题

bug场景&#xff1a; 一切从一次换源说起…叭叭叭 这篇文章解决的问题&#xff1a; 1.换源&#xff0c;默认源太慢&#xff0c;换成可用的阿里云的源 2.apt-get failed to …问题 3.图形化异常问题 4.get unmet dependence 问题 5. 鼠标光标消失和鼠标变成叉叉问题。 解决方…

指针篇章(3)-(指针之间的区别)

学习目录 ————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————…

Windows和Linux(Ubuntu)双系统下,完全删除Ubuntu系统

彻底删除Windows和Linux&#xff08;Ubuntu&#xff09;双系统下的Ubuntu系统及其开机引导项 &#xff08;一&#xff09;删除Ubuntu系统占用的磁盘分区&#xff08;在Windows下操作&#xff09; 1.右键单击“此电脑”&#xff0c;点击“管理”&#xff1b; 2.点击左侧的“…

分布式之SpringCloud

一、SpringCloud 1、SpringCloud是什么 Spring Cloud是一系列框架的有序集合&#xff0c;这些框架为我们提供了分布式系统构建工具。 2、SpringCloud包含那些项目 项目项目名称服务注册于发现Alibaba Nacos、Netflix Eureka、Apache Zookper分布式配置中心Alibaba Nacos、S…

SpringBoot项目如何部署到服务器

文章目录 准备&#xff1a;方式一&#xff1a;Jar包方式&#xff08;推荐&#xff09;部署步骤&#xff1a; 方式二&#xff1a;War包方式部署步骤&#xff1a; 总结 准备&#xff1a; 云服务器&#xff08;阿里云、腾讯云等&#xff09;Linux系统以及运行所需的环境 方式一&a…

GIS在地质灾害危险性评估与灾后重建中的应用

地质灾害是指全球地壳自然地质演化过程中&#xff0c;由于地球内动力、外动力或者人为地质动力作用下导致的自然地质和人类的自然灾害突发事件。由于降水、地震等自然作用下&#xff0c;地质灾害在世界范围内频繁发生。我国除滑坡灾害外&#xff0c;还包括崩塌、泥石流、地面沉…

【牛客】VL68 同步FIFO

描述 请设计带有空满信号的同步FIFO&#xff0c;FIFO的深度和宽度可配置。双口RAM的参考代码和接口信号已给出&#xff0c;请在答案中添加并例化此部分代码。 电路的接口如下图所示。端口说明如下表。 接口电路图如下&#xff1a; 双口RAM端口说明&#xff1a; 端口名I/O描述…

【JAVA】CSS3伸缩盒案例、响应式布局、BFC

1.CSS3伸缩盒案例 效果&#xff1a;用伸缩盒模型 <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title>&…

深入理解Java多线程与线程池:提升程序性能的利器

✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天开心哦&#xff01;✨✨ &#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; 目录 引言 一、实现多线程 1.1. 继承Thread类 1.2. 实现Runnab…