[Linux]互斥锁(什么是锁,为什么需要锁,怎么使用锁(接口),演示代码)

news2024/9/20 18:29:04

目录

 

一、锁的概念

一些需要了解的概念

什么是锁?为什么需要锁?什么时候使用锁?怎么定义锁?

二、锁的接口

1.初始化锁

2.加锁

3.申请锁

4.解锁

5.销毁锁

三、实践(写代码):黄牛抢票

Makefile

test.cc

结果


 

一、锁的概念

一些需要了解的概念

  • 临界资源:任一时刻只允许一个线程访问的共享资源
  • 临界区:访问临界资源的代码
  • 原子性:不会被任何调度机制打断的操作,该操作只有两态:要么完成,要么未完成
  • 互斥:任何时刻,互斥可以保证有且只有一个执行流进入临界区,访问临界资源,通常对临界资源起保护作用,而锁就是实现互斥的。
  • 同步:同步是一种机制,用于协调不同进程、线程或设备之间的操作,确保它们按照预期的顺序和方式进行。同步的目的是保持数据的一致性和系统的稳定性。

什么是锁?为什么需要锁?什么时候使用锁?怎么定义锁?

什么是锁?

锁是一种同步机制,用于控制多个线程对共享资源的访问。通过锁,可以确保一次只有一个线程能够访问特定的代码段或数据,从而防止数据竞争和不一致。锁的主要目的是确保数据的一致性和线程安全性。

为什么需要锁?

需要锁的主要原因在于确保多线程或多用户环境中共享资源访问的原子性和数据一致性。在多线程应用中,若多个线程同时访问并修改同一资源,可能导致数据冲突、不一致甚至损坏。

故事说明:把线程比作一个学生,小明,锁是自习室的门口上的锁头,当小明要进自习室时,他就从墙上拿钥匙(钥匙只有一把)解锁进入教室,而此时的自习室就是临界区,小明在自习室的书本,本子,笔就是临界资源,当小明突然要上厕所离开自习室时,因为自习室里有小明的书本呀,笔呀,等等东西,所以小明离开时就把门锁上了。小明自习了一天了,到晚上了吃饭了,小明不想自习了,小明带上他的东西离开,然后 把门锁上,把钥匙挂回墙上,此时其他同学就可以使用自习室了。在这例子中锁的作用就是只允许有钥匙的学生进入自习室,不允许其他没有钥匙的同学进入(其他线程),换言之锁的作用就是实现在一个临界区中的任一时刻只允许一个线程进入,访问。在这个故事中如果自习室门上没有锁,当小明要上厕所时,别的同学可以可以进入自习室破坏,拿走小明的东西呢?答案是有可能的,所以为了保证自习室里的小明的资源的安全,所以需要锁,把门上锁。

什么时候使用锁?

使用锁主要在多线程或多用户环境中,当多个线程或用户需要并发访问和修改共享资源时。锁能确保同一时间只有一个线程或用户访问资源,避免数据冲突和不一致。在需要保证数据完整性、原子性和安全性的场景下,应使用锁来同步和控制对共享资源的访问。

在Linux中锁长什么样呢?我们怎么定义锁呢?

pthread_mutex_t是一个用于线程同步的互斥锁类型。例如:pthread_mutex_t mutex:定义互斥锁变量mutex,mutex就是一个锁(锁变量)。

二、锁的接口

1.初始化锁

 

功能:用于初始化锁变量
原型

#include <pthread.h>

锁为局部变量时使用如下函数初始化

int pthread_mutex_init(pthread_mutex_t *restrict mutex,
              const pthread_mutexattr_t *restrict attr);

锁为全局变量时使用如下方法初始化

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
 

参数

  1. pthread_mutex_t *restrict mutex:指向要初始化的互斥锁变量的指针。这个指针指向的互斥锁变量在调用 pthread_mutex_init 之前应该是未初始化的。

  2. const pthread_mutexattr_t *restrict attr:指向互斥锁属性的指针。这个参数是可选的,通常可以传递 NULL (nullptr)来使用默认的互斥锁属性。如果你需要设置特定的属性(例如互斥锁的类型),你需要先使用 pthread_mutexattr_init 初始化一个 pthread_mutexattr_t 变量,然后设置所需的属性,最后将其传递给 pthread_mutex_init

返回值:成功返回0;失败返回错误码

使用例子:

#include <iosteam>
#include <pthread.h>
using namespace std;

//全局锁初始化方式
pthread_mutex_t global_mutex = PTHREAD_MUTEX_INITIALIZER;//定义并初始化锁

int main()
{
    //局部初始化方式
    pthread_mutex_t local_mutex;//定义锁
    pthread_mutex_init(&local_mutex, nullptr);//初始化锁
    //加锁
    //...
    //解锁
    //销毁锁
    return 0;
}

 

2.加锁

功能:获取(锁定)互斥锁,如果锁当前未被其他线程占用(即锁是“空闲”的),那么调用此函数的线程将成功获取锁,并可以继续执行其临界区代码。如果锁已被其他线程占用,则调用线程将被阻塞,直到锁被释放(即被当前持有锁的线程调用 pthread_mutex_unlock
原型

#include <pthread.h>

int pthread_mutex_lock(pthread_mutex_t *mutex);

参数

pthread_mutex_t *mutex:是一个指向互斥锁变量的指针。

返回值

pthread_mutex_lock 函数的返回值用于指示加锁操作是否成功。

  • 如果成功获取锁,函数返回 0
  • 如果在尝试获取锁时发生错误(例如,由于无效的互斥锁指针或系统资源不足),函数将返回一个错误码(非零值)。具体的错误码可以根据不同的系统和库实现而有所不同,但通常会遵循 POSIX 线程标准中定义的错误码。

注意事项

  • 使用 pthread_mutex_lock 时,必须确保在不再需要锁时调用 pthread_mutex_unlock 来释放锁,以避免死锁。
  • 如果在调用 pthread_mutex_lock 后线程被中断或取消,锁可能仍然处于锁定状态,需要特别小心处理。
  • 如果互斥锁的类型是递归锁(recursive mutex),则同一个线程可以多次获取同一个锁,但每次获取锁后都必须对应地释放锁。

3.申请锁

功能:尝试获取一个互斥锁(mutex),而不会阻塞调用线程。如果互斥锁已经被其他线程持有,则 pthread_mutex_trylock 不会使调用线程进入睡眠状态等待锁释放,而是立即返回表示失败的错误码。
原型

#include <pthread.h>

int pthread_mutex_trylock(pthread_mutex_t *mutex);
参数

pthread_mutex_t *mutex:是一个指向互斥锁变量的指针。
返回值

  • 如果成功获取锁,函数返回 0
  • 如果锁已被其他线程占用,或者发生其他错误(如传递了无效的互斥锁指针),函数将返回一个错误码。常见的错误码包括 EBUSY(表示锁当前被其他线程占用)和 EINVAL(表示传递给函数的互斥锁是无效的)。

4.解锁

功能:用于释放互斥锁(mutex),在多线程编程中用于确保线程同步的正确性。当一个线程完成对共享资源的访问后,它应该调用 pthread_mutex_unlock 来释放锁,以便其他线程能够获取该锁并访问相同的资源。
原型

#include <pthread.h>

int pthread_mutex_unlock(pthread_mutex_t *mutex);

参数

pthread_mutex_t *mutex:是一个指向互斥锁变量的指针。
返回值

  • 如果成功释放锁,函数返回 0
  • 如果在尝试解锁时发生错误(例如,传递给函数的互斥锁是无效的,或者当前线程没有持有该锁),函数将返回一个错误码。常见的错误码包括 EINVAL(表示传递给函数的互斥锁是无效的)和 EPERM(表示当前线程没有持有该锁)。

5.销毁锁

功能:用于销毁(释放)互斥锁(mutex)的函数。在多线程编程中,当不再需要某个互斥锁时,应该调用 pthread_mutex_destroy 来销毁它,以释放相关资源。
原型

#include <pthread.h>

int pthread_mutex_destroy(pthread_mutex_t *mutex);

参数

pthread_mutex_t *mutex:是一个指向互斥锁变量的指针。
返回值

  • 如果成功销毁锁,函数返回 0
  • 如果在尝试销毁锁时发生错误(例如,传递给函数的互斥锁是无效的,或者锁仍被持有),函数将返回一个错误码。常见的错误码包括 EBUSY(表示锁当前被其他线程占用)和 EINVAL(表示传递给函数的互斥锁是无效的)。

三、实践(写代码):黄牛抢票

说明:用多线程模拟黄牛,常数模拟票

Makefile

test:test.cc
	g++ -o $@ $^ -std=c++11 -lpthread
PHONY:clean
clean:
	rm -f test;

test.cc

#include <iostream>
#include <string>
#include <unistd.h>
#include <pthread.h>
#include <vector>
using namespace std;
int ticket = 1000; // 一千张票
int threadnum = 5; // 黄牛数
// 定义全局锁
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *threadtask(void *args)
{

    while (ticket > 0)
    {
        string name = static_cast<char *>(args);
        // 加锁
        pthread_mutex_lock(&mutex);
        cout << "我是: " << name << "我抢到了" << ticket << "号票" << endl;
        ticket--; // 票数--
        sleep(1);
        // 解锁
        pthread_mutex_unlock(&mutex);
    
    }

    return nullptr;
}
int main()
{
    // 五个黄牛
    vector<pthread_t> threads;
    for (int i = 0; i < threadnum; i++)
    {
        pthread_t tid;
        char threadname[64];
        sprintf(threadname, "thraed-%d", i + 1);
        pthread_create(&tid, nullptr, threadtask, threadname);
        threads.push_back(tid);
    }
    void *ret;
    for (int i = 0; i < threads.size(); i++)
    {
        pthread_join(threads[i], &ret);//等待线程,回收资源
    }
    // 销毁锁
    pthread_mutex_destroy(&mutex);
    return 0;
}

结果

全是5号线程抢到票,原因是Linux内核中的线程调度器根据线程的优先级、状态和其他因素来决定哪个线程应该被调度执行。如果某个线程的优先级高于其他线程,或者其状态更适合执行(例如,它已准备好运行并且没有受到阻塞),那么它就更有可能被调度器选中。你可以在你的linux中试试或许会有不同的结果。

0c1a3fe630b84286af006feae9802cef.png

 完结!!!

 

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

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

相关文章

C#开发中方法使用的问题注意

C#开发中&#xff0c;我们在进行方法内嵌时&#xff0c;需要注意方法回传带值时&#xff0c;我们需要对方法回传的值进行一个赋值传递 如下所示 console.WriteLine("请输入你的爱好&#xff1a;"); string aihao Console.ReadLine(); name ChangeData(name);同时在…

Legacy|电脑Windows系统如何迁移到新安装的硬盘?系统迁移详细教程!

前言 前面讲了很多很多关于安装系统、重装系统的教程。但唯独没有讲到电脑换了新的硬盘之后&#xff0c;怎么把旧系统迁移到新的硬盘上。 今天小白就来跟各位小伙伴详细唠唠&#xff1a; 开始之前需要把系统迁移的条件准备好&#xff0c;意思就是在WinPE系统下&#xff0c;可…

B004-springcloud alibaba 服务容错 Sentinel

目录 高并发带来的问题服务雪崩效应常见容错方案常见的容错思路隔离超时限流熔断降级 常见的容错组件 Sentinel入门什么是Sentinel微服务项目集成Sentinel核心库安装Sentinel控制台实现一个接口的限流 Sentinel的概念和功能基本概念重要功能 Sentinel规则流控规则三种流控模式三…

拒绝云测,热门猫主食冻干对比测评,希喂、SC、VE谁实力更强?

在当今的科学养宠时代&#xff0c;主食冻干已经成为了猫日常饮食不可或缺的一部分。高肉含量的主食冻干不仅易吸收、好消化&#xff0c;更能给猫提供其他猫粮所不能提供的微量物质&#xff0c;更满足猫的全面营养需求。然而&#xff0c;在众多品牌和口味的主食冻干中&#xff0…

抖音找人推广要给多少推广费?CloudNEO:9000+网红资源,助您品牌代言

抖音作为中国最受欢迎的短视频平台之一&#xff0c;吸引了众多用户的关注&#xff0c;也成为了企业推广的热门渠道。然而&#xff0c;很多人对于在抖音上找人推广需要支付多少推广费并不了解。下面让我们来解析一下抖音推广费用的计算方式。 1. 推广形式&#xff1a; 首先&…

ISP技术综述

原文来自技术前沿&#xff1a;ISP芯片终极进化——VP芯片&#xff08;AI视觉处理器&#xff09; 目录 1.计算机视觉的定义 2.与计算机视觉密切相关的概念与计算机视觉密切相关的概念有机器视觉&#xff0c;图像处理与分析&#xff0c;图像和视频理解。 3.计算机视觉的应用 …

[自研开源] MyData v0.7.3 更新日志

开源地址&#xff1a;gitee | github 详细介绍&#xff1a;MyData 基于 Web API 的数据集成平台 部署文档&#xff1a;用 Docker 部署 MyData 使用手册&#xff1a;MyData 使用手册 试用体验&#xff1a;https://demo.mydata.work 交流Q群&#xff1a;430089673 介绍 MyData …

git常见使用

1. 概念 分布式&#xff0c;有远程仓库和本地仓库的概念&#xff0c;因此要注意同步问题git是面向对象的&#xff0c;本质是内容寻址系统。.git目录下有个文件夹objects&#xff0c;存储git库中的对象&#xff0c;git就是根据object建立一种树形结构&#xff0c;将文件和通过h…

数据库应用:Linux 部署 GaussDB

目录 一、实验 1.环境 2.Linux 部署 GaussDB 3.Linux 使用 GaussDB 4.使用 GaussDB 进行表与索引操作 5.使用 GaussDB 进行视图操作 6.使用 GaussDB 进行联表查询 7.使用 GaussDB 进行外键关联 二、问题 1.运行python脚本报错 2. 安装GaussDB 报错 3. install 安装…

广州大彩科技新品发布:大彩科技COF系列2.4寸串口屏发布!

一、产品介绍 此次发布的是S系列平台2.4寸COF超薄结构串口屏&#xff0c;分辨率为240*320&#xff0c;该平台采用了Cortex-M3内核的处理器&#xff0c;内置了2Mbyte PSRAM和64Mbit FLASH&#xff0c;是专为小尺寸串口屏设计的MCU&#xff0c;精简了外围电路。 该平台默认支持大…

【sql】深入理解 mysql的EXISTS 语法

相关文章&#xff1a; 【sql】深入理解 mysql的EXISTS 语法 【sql】初识 where EXISTS 1. 使用格式如下&#xff1a; select * from a where exists ( 任何子查询 ) 代码根据颜色分成两段&#xff0c;前面的是主查询&#xff0c;后面红色的是子查询&#xff0c;先主后子&…

从政府工作报告探计算机行业发展

文章目录 每日一句正能量前言以“数”谋新、加“数”向实人工智能方面人工智能成核心驱动引擎 软件方面通信方面后记 每日一句正能量 该来的始终会来&#xff0c;千万别太着急&#xff0c;如果你失去了耐心&#xff0c;就会失去更多。该走过的路总是要走过的&#xff0c;从来不…

Android14之HIDL报错:Invalid sparse file format at header magic(一百九十六)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

【数据结构与算法】(17):计数排序和基数排序详解

&#x1f921;博客主页&#xff1a;Code_文晓 &#x1f970;本文专栏&#xff1a;数据结构与算法 &#x1f63b;欢迎关注&#xff1a;感谢大家的点赞评论关注&#xff0c;祝您学有所成&#xff01; ✨✨&#x1f49c;&#x1f49b;想要学习更多数据结构与算法点击专栏链接查看&…

智慧城市与数字孪生:科技融合助力城市可持续发展

随着信息技术的迅猛发展&#xff0c;智慧城市和数字孪生作为现代城市发展的重要理念和技术手段&#xff0c;正日益受到广泛关注。智慧城市通过集成应用先进的信息通信技术&#xff0c;实现城市管理、服务、运行的智能化&#xff0c;而数字孪生则是利用数字化手段对物理城市进行…

面试笔记——Redis(使用场景、面临问题、缓存穿透)

Redis的使用场景 Redis&#xff08;Remote Dictionary Server&#xff09;是一个内存数据结构存储系统&#xff0c;它以快速、高效的特性闻名&#xff0c;并且它支持多种数据结构&#xff0c;包括字符串、哈希表、列表、集合、有序集合等。它主要用于以下场景&#xff1a; 缓…

libmodbus编译为64位动态库

通用方法&#xff0c;记录一下&#xff0c;以便后续参考。 Step 1. 下载libmodbus源码 GitHub - stephane/libmodbus: A Modbus library for Linux, Mac OS, FreeBSD and Windows Step 2. 生成配置文件 进入libmodbus-master\src\win32目录&#xff0c;在该目录下打开终端&am…

【火猫TV】DOTA2 BB队员称:队伍非常具有凝聚力

1、近日BB战队队员Nightfall接受采访时表示战队自从去年获得梦幻联赛S20亚军以来&#xff0c;就非常具有凝聚力。 “我认为战队自从去年获得梦幻联赛S20亚军以来&#xff0c;战队就非常具有凝聚力。从那一刻开始&#xff0c;我们这群人不再只是几个选手组建起来的松散组织&…

移动云COCA架构实现算力跃升,探索人工智能新未来

近期&#xff0c;随着OpenAI正式发布首款文生视频模型Sora&#xff0c;标志着人工智能大模型在视频生成领域有了重大飞跃。Sora模型不仅能够生成逼真的视频内容&#xff0c;还能够模拟物理世界中的物体运动与交互&#xff0c;其核心在于其能够处理和生成具有复杂动态与空间关系…

C++的语法

可能需要用到存储各种数据类型&#xff08;比如字符型、宽字符型、整型、浮点型、双浮点型、布尔型等&#xff09; 下表显示了各种变量类型在内存中存储值时需要占用的内存&#xff0c;以及该类型的变量所能存储的最大值和最小值。 注意&#xff1a;不同系统会有所差异 #inc…