【互斥锁与信号量】线程通信:互斥锁(mutex)与信号量(semaphore)

news2024/12/23 15:00:41

目录

0. 互斥锁与信号量

同步互斥概述

1. 互斥锁

1.1 互斥锁的概念

1.2 互斥锁初始化:pthread_mutex_init函数

1.3 互斥锁上锁:pthread_mutex_lock函数

1.4 互斥锁解锁:pthread_mutex_unlock函数

1.5 销毁互斥锁:pthread_mutex_destroy函数

1.6 互斥锁案例

1.61 不使用互斥锁的结果

 1.62 使用互斥锁的结果 

2. 信号量

2.1 信号量的概念

2.2 信号量的初始化:sem_init函数

2.3 信号量的P操作:sem_wait函数

2.4 信号量的V操作:sem_post函数

2.5 获取信号量的计数值:sem_getvalue函数

2.6 信号量的销毁:sem_destroy函数

2.7 信号量的使用

2.7.1 信号量实现互斥功能

2.7.2 信号量实现同步功能

 总结:


0. 互斥锁与信号量

同步互斥概述

        在多任务操作系统中,同时运行的多个任务可能都需要访问/使用同一种资源。多个任务之间有依赖关系,某个任务的运行依赖于另一个任务。

        同步和互斥就是用于解决这两个问题的。

互斥:

        一个公工资源同一时刻只能被一个进程或线程使用,多个进程或线程不能同时使用公共资源。POSIX标准中进程和线程同步和互斥的方法,主要有信号量和互斥锁两种方式。

同步:

        两个或两个以上的进程或线程在运行过程中协同步调,按预定的先后次序运行。

        同步就是在互斥的基础上有顺序。

1. 互斥锁

1.1 互斥锁的概念

        mutex是一种简单的加锁的方法来控制对共享资源的访问,mutex只有两种状态,即上锁(lock)和解锁(unlock)。在访问该资源前,首先应申请mutex。

  •         如果mutex处于unlock状态,则会申请到mutex并立即lock。
  •         如果mutex处于lock状态,则默认阻塞申请者。unlock操作应该由lock者进行。

初始化互斥锁:pthread_mutex_init函数。

        mutex用pthread_mutex_t数据类型表示,在使用互斥锁前,必须先对它进行初始化。

静态分配的互斥锁:

        pthread_mutex_t mutex =   PTHREAD_MUTEX_INITIALIZER;

动态分配互斥锁:

        pthread_mutex_t mutex;

销毁互斥锁:pthread_mutex_destroy

1.2 互斥锁初始化:pthread_mutex_init函数

pthread_mutex_init函数:

#include<pthread.h>

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

功能:

        初始化一个互斥锁。

参数:

        mutext:互斥锁的地址。

        attr:互斥锁的属性,NULL为默认的属性。

返回值:

        成功:0

        失败:非0

1.3 互斥锁上锁:pthread_mutex_lock函数

pthread_mutex_lock函数

#include<pthread.h>

int pthread_mutex_lock(pthread_mutex_t *mutex);

功能:

        对互斥锁上锁,若已经上锁,则调用者一直阻塞到互斥锁解锁。

参数:

        mutex:指定的互斥锁。

返回值:

        成功:0

        失败:非0

#include<pthread.h>

int pthread_mutex_trylock(pthread_mutex_t * mutex);

功能:

        对互斥锁上锁,若已经上锁,则上锁失败,函数立即返回。

参数:

        mutex:互斥锁地址。

返回值:

        成功:0

        失败:非0 

1.4 互斥锁解锁:pthread_mutex_unlock函数

pthread_mutex_unlock函数

#include<pthread.h>

int pthread_mutex_unlock(pthread_mutex_t *mutex);

功能:

        对指定的互斥锁解锁。

参数:

        mutex:互斥锁地址。

返回值:

        成功:0

        失败:非0

1.5 销毁互斥锁:pthread_mutex_destroy函数

pthread_mutex_destroy函数

#include<pthread.h>

int pthread_mutex_destroy(pthread_mutex_t *mutex);

功能:

        销毁指定的一个互斥锁。

参数:

        mutex:互斥锁地址。

返回值:

        成功:0

        失败:非0

1.6 互斥锁案例

1.61 不使用互斥锁的结果

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

int money = 10000;

void* pthread_fun1(void* arg)
{
    int get, yu, shiji;
    get = 10000;

    printf("zhangsan look balance\n");
    sleep(1);
    yu = money;

    printf("zhangsan spent money\n");
    sleep(1);
    if (get > yu)
    {
        shiji = 0;

    }
    else
    {
        shiji = get;
        yu = yu - get;
        money = yu;
    }
    printf("zhangsan want spent %d, fact spent is %d, balance is %d\n", get, shiji, yu);
    pthread_exit(NULL);
}
void* pthread_fun2(void* arg)
{
    int get, yu, shiji;
    get = 10000;

    printf("lisi look balance\n");
    sleep(1);
    yu = money;

    printf("lisi spent money\n");
    sleep(1);
    if (get > yu)
    {
        shiji = 0;

    }
    else
    {
        shiji = get;
        yu = yu - get;
        money = yu;
    }
    printf("lisi want spent %d, fact spent is %d, balance is %d\n", get, shiji, yu);
    pthread_exit(NULL);
}
int main()
{

    pthread_t thread1, thread2;

    if (pthread_create(&thread1, NULL, pthread_fun1, NULL) != 0)
    {
        perror("fail to pthread_create");
        exit(1);
    }
    if (pthread_create(&thread2, NULL, pthread_fun2, NULL) != 0)
    {
        perror("fail to pthread_creat");
        exit(1);
    }

    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
    return 0;
}

执行截图:

 1.62 使用互斥锁的结果 

代码示例:

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

int money = 10000;
//第一步:创建互斥锁
pthread_mutex_t mutex;

void* pthread_fun1(void* arg)
{
    int get, yu, shiji;
    get = 10000;
    //第三步,互斥锁上锁
    pthread_mutex_lock(&mutex);
    printf("zhangsan look balance\n");
    sleep(1);
    yu = money;

    printf("zhangsan spent money\n");
    sleep(1);
    if (get > yu)
    {
        shiji = 0;

    }
    else
    {
        shiji = get;
        yu = yu - get;
        money = yu;
    }
    printf("zhangsan want spent %d, fact spent is %d, balance is %d\n", get, shiji, yu);
    //第四步,互斥锁解锁
    pthread_mutex_unlock(&mutex);
    pthread_exit(NULL);
}
void* pthread_fun2(void* arg)
{
    int get, yu, shiji;
    get = 10000;
    //第三步,互斥锁上锁
    pthread_mutex_lock(&mutex);
    printf("lisi look balance\n");
    sleep(1);
    yu = money;

    printf("lisi spent money\n");
    sleep(1);
    if (get > yu)
    {
        shiji = 0;

    }
    else
    {
        shiji = get;
        yu = yu - get;
        money = yu;
    }
    printf("lisi want spent %d, fact spent is %d, balance is %d\n", get, shiji, yu);
    //第四步,互斥锁解锁
    pthread_mutex_unlock(&mutex);
    pthread_exit(NULL);
}
int main()
{
    //第二步,初始化互斥锁
    pthread_mutex_init(&mutex, NULL);
    pthread_t thread1, thread2;

    if (pthread_create(&thread1, NULL, pthread_fun1, NULL) != 0)
    {
        perror("fail to pthread_create");
        exit(1);
    }
    if (pthread_create(&thread2, NULL, pthread_fun2, NULL) != 0)
    {
        perror("fail to pthread_creat");
        exit(1);
    }

    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
    //第五步,销毁互斥锁
    pthread_mutex_destroy(&mutex);
    return 0;
}

执行结果:

2. 信号量

2.1 信号量的概念

        信号量广泛用于进程或线程间的同步和互斥,信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。

        编程时可根据操作信号量值的结果判断是否对公共资源具有访问的权限,当信号量值大于0时,则可以访问,否则将阻塞。

        信号量又称之为PV操作,PV是对信号量的操作,一次P操作使信号量sem减1,一次V操作使信号量sem+1。对于P操作,如果信号量的sem值为小于等于0,则P操作就会阻塞,如果信号量的值大于0,才可以执行P操作进行减1。

信号量主要用于进程或线程间的同步和互斥这两种典型情况。

  • 若用于互斥,几个进程(或线程)往往只设置一个信号量。
  • 若用于同步操作,往往会设置多个信号量,并且安排不同的初始值,来实现它们之间的执行顺序。

信号量用于互斥:

信号量用于同步:

 

2.2 信号量的初始化:sem_init函数

sem_init函数

#include<semaphore.h>

int sem_init(sem_t *sem, int pshared, unsigned int value);

功能:

        创建一个信号量并初始化它的值。

参数:

        sem:信号量的地址。

        pshared:等于0,信号量在线程间共享;不等于0,信号量在进程间共享。

        value:信号量的初始值。

返回值:

        成功:0

        失败:-1

2.3 信号量的P操作:sem_wait函数

sem_wait函数

#include<semaphore.h>

int sem_wait(sem_t *sem);

功能:

        将信号量的值减1,若信号量的值小于等于0,此函数会引起调用者阻塞。

参数:

        sem:信号量的地址。

返回值:

        成功:0

        失败:-1

#include<semaphore.h>

int sem_trywait(sem_t *sem);

功能:

        将信号量的值减1,若信号量的值小于0,则对信号量的操作失败,函数立即返回。

参数:

        sem:信号量地址。

返回值:

        成功:0

        失败:-1

2.4 信号量的V操作:sem_post函数

sem_post函数

#include<semaphore.h>

int sem_post(sem_t *sem);

功能:

        将信号量的值加1并发出信号唤醒等待线程。

参数:

        sem:信号量地址。

返回值:

        成功:0

        失败:-1 

2.5 获取信号量的计数值:sem_getvalue函数

sem_getvalue函数

#include<semaphore.h>

int sem_getvalue(sem_t *sem, int *sval);

功能:

        获取sem标识的信号量的值,保存在sval中。

参数:

        sem:信号量地址。

        sval:保存信号量值的地址。

返回值:

        成功:0

        失败:-1

2.6 信号量的销毁:sem_destroy函数

sem_destroy函数

#include<semaphore.h>

int sem_destroy(sem_t *sem);

功能:        

        删除sem标识的信号量。

参数:

        sem:信号量地址。

返回值:

        成功:0

        失败:-1

2.7 信号量的使用

2.7.1 信号量实现互斥功能

代码示例:

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

//第一步,创建信号量
sem_t sem;

void printer(char* str)
{
    //第三步,P操作
    sem_wait(&sem);
    while (*str)
    {
        putchar(*str);
        fflush(stdout);
        str++;
        sleep(1);
    }
    //第四步,V操作
    sem_post(&sem);
}

void* thread_fun1(void* arg)
{
    char* str1 = "hello";
    printer(str1);
}
void* thread_fun2(void* arg)
{
    char* str2 = "world";
    printer(str2);
}

int main()
{
    //第二步,初始化信号量
    sem_init(&sem, 0, 1);
    pthread_t tid1, tid2;

    pthread_create(&tid1, NULL, thread_fun1, NULL);
    pthread_create(&tid2, NULL, thread_fun2, NULL);

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);

    printf("\n");
    //第五步,销毁信号量
    sem_destroy(&sem);
    return 0;
}

执行截图:

2.7.2 信号量实现同步功能

代码示例:

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

char ch = 'a';
//第一步,创建两个信号量
sem_t sem_g, sem_p;

void* pthread_g(void* arg)
{
    while (ch <= 'z')
    {
        //第四步,后执行线程
        sem_wait(&sem_g);
        ch++;
        sleep(1);
        //第六步,V操作
        sem_post(&sem_p);
    }
}

void* pthread_p(void* arg)
{
    while (ch < 'z')
    {
        //第三步,先执行线程
        sem_wait(&sem_p);
        printf("%c", ch);
        fflush(stdout);
        //第五步,V操作
        sem_post(&sem_g);
    }
}
int main()
{
    //第二步,初始化信号量
    sem_init(&sem_g, 0, 0);
    sem_init(&sem_p, 0, 1);
    pthread_t tid1, tid2;

    pthread_create(&tid1, NULL, pthread_g, NULL);
    pthread_create(&tid2, NULL, pthread_p, NULL);

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);

    printf("\n");
    //第七步,销毁信号量
    sem_destroy(&sem_g);
    sem_destroy(&sem_p);
    return 0;
}

执行截图:

 总结:

        总的来说,互斥锁和信号量都是线程和进程同步的重要工具。互斥锁主要用于保护资源,保证同一时间只有一个线程或进程访问某一资源,从而避免并发问题。而信号量更多的是用于线程和进程之间的通信和同步,控制在一定范围内的并发访问,为程序提供更细粒度的并发控制。

        掌握互斥锁和信号量的使用和区别,能极大地提高多线程和多进程编程的效率和稳定性。

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

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

相关文章

chatgpt赋能python:Python重写父类方法:在OOP编程中的应用

Python重写父类方法&#xff1a;在OOP编程中的应用 在Python的面向对象编程范式中&#xff0c;继承是一种非常重要的概念。当我们声明一个类时&#xff0c;我们可以通过继承来扩展类的功能并避免重复编写代码。在这个过程中&#xff0c;很可能你会碰到需要重写父类方法的情况。…

17.RocketMQ之死信队列

highlight: arduino-light 1.5 死信队列 当一条消息初次消费失败&#xff0c;消息队列 RocketMQ 会自动进行消息重试&#xff1b;达到最大重试次数后&#xff0c;若消费依然失败&#xff0c;则表明消费者在正常情况下无法正确地消费该消息&#xff0c;此时&#xff0c;消息队列…

flutter 简介 flutter 能为我们做什么

flutter 简介 flutter 能为我们做什么 前言一、什么是Flutter&#xff1f;二、Flutter的特点和优势三、Flutter与其他跨平台框架的比较总结 前言 陆陆续续已经写了60多篇的flutter 的文章了&#xff0c;本篇文章就来说说我对flutter 的简单看法 一、什么是Flutter&#xff1f…

【Rust】安装

文章目录 1.官网下载2.安装3.安装验证4.打开本地文档5.安装插件6.HelloWorld①新建项目目录使用VSCode打开②新建rs文件③编译④运行 7.HelloCargo①新建项目目录使用VSCode打开②cargo build③cargo run④cargo check⑤为发布构建 8.更新与卸载 1.官网下载 官网地址&#xff…

c++11 标准模板(STL)(std::basic_ostream)(一)

定义于头文件 <ostream> template< class CharT, class Traits std::char_traits<CharT> > class basic_ostream : virtual public std::basic_ios<CharT, Traits> 类模板 basic_ostream 提供字符流上的高层输出操作。受支持操作包含有格式…

工业读码器在工业生产上应用的优势有哪些?

工业读码器是一种用于读取和解码条形码、二维码等信息的设备&#xff0c;一般广泛应用于工业生产中。可以辅助企业进行工业生产流程、物料等方面的管理。下面我们就一起来了解一下&#xff0c;工业读码器在工业生产上应用的优势有哪些&#xff1f; 工业读码器在工业生产上应用…

基于Java学生公寓管理系统设计实现(源码+lw+部署文档+讲解等)

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

华为OD机试真题 Python 实现【工作安排】【2023Q1 100分】

目录 一、题目描述二、输入描述三、输出描述四、解题思路五、Python算法源码六、效果展示1、输入2、输出3、说明 一、题目描述 小明每周上班都会拿着自己的工作清单&#xff0c;工作清单内包含n项工作&#xff0c;每项工作都有对应的耗时时长&#xff08;单位h&#xff09;和报…

回归预测 | MATLAB实现基于QPSO-BiLSTM、PSO-BiLSTM、BiLSTM多输入单输出回归预测

回归预测 | MATLAB实现基于QPSO-BiLSTM、PSO-BiLSTM、BiLSTM多输入单输出回归预测 目录 回归预测 | MATLAB实现基于QPSO-BiLSTM、PSO-BiLSTM、BiLSTM多输入单输出回归预测效果一览基本描述程序设计参考资料 效果一览 基本描述 1.Matlab实现QPSO-BiLSTM、PSO-BiLSTM、BiLSTM神经…

【综合布线技术】网络杂谈(17)之什么是综合布线系统

涉及知识点 什么是综合布线系统&#xff0c;综合布线的特点&#xff0c;综合布线的标准&#xff0c;综合布线 6 个子系统&#xff0c;综合布线系统的构成&#xff0c;深入了解综合布线技术。 原创于&#xff1a;CSDN博主-《拄杖盲学轻声码》&#xff0c;更多内容可去其主页关注…

chatgpt赋能python:Python量化指标库介绍

Python量化指标库介绍 Python是一种高级编程语言&#xff0c;因其简单易用、开源免费、生态环境完备等优点&#xff0c;已成为量化分析领域的首选编程语言之一。随着金融市场越来越复杂&#xff0c;金融量化分析的需求也日益增长。为了满足这一需求&#xff0c;Python量化指标…

【OJ比赛日历】快周末了,不来一场比赛吗? #07.01-07.07 #11场

CompHub[1] 实时聚合多平台的数据类(Kaggle、天池…)和OJ类(Leetcode、牛客…&#xff09;比赛。本账号会推送最新的比赛消息&#xff0c;欢迎关注&#xff01; 以下信息仅供参考&#xff0c;以比赛官网为准 目录 2023-07-01&#xff08;周六&#xff09; #3场比赛2023-07-02…

Yarn的实现原理

Yarn作为分布式集群的资源调度框架&#xff0c;它的出现伴随着Hadoop的发展&#xff0c;使Hadoop从一个单一的大数据计算引擎&#xff0c;成为一个集存储、计算、资源管理为一体的完整大数据平台&#xff0c;进而发展出自己的生态体系&#xff0c;成为大数据的代名词。 Yarn的发…

谷歌云:全面推出 AlloyDB for PostgreSQL 与数据库迁移服务

【本文由Cloud Ace 整理发布。Cloud Ace 是谷歌云全球战略合作伙伴&#xff0c;拥有 300 多名工程师&#xff0c;也是谷歌最高级别合作伙伴&#xff0c;多次获得 Google Cloud 合作伙伴奖。作为谷歌托管服务商&#xff0c;我们提供谷歌云、谷歌地图、谷歌办公套件、谷歌云认证培…

在Android Studio 中运行React Native 项目

项目根目录执行命令安装开发依赖 yarn检查项目SDK、NDK、JDK否配置正确 点击 Android Studio 里点击大象 全部下载完毕&#xff0c;点击运行按钮&#xff0c;编译项目 连接真机的两种方式 无线连接 adb devices adb tcpip 5555 #连接端口默认5555 adb connect 192.168.0…

低功耗测距语音方案,4路PWM调光,三合一语音芯片WTV890-B004

随着智能门锁市场的不断发展&#xff0c;人们对于智能化、便捷化的需求也越来越高。在这个背景下&#xff0c;深圳唯创知音研发出了三合一语音芯片——WTV890-B004。这款创新产品集低功耗红外测距、语音播放和4路PWM调光功能于一体&#xff0c;为您打造一个高效、智能化方案。 …

上传自己的npm依赖包

有时候我们需要对某个依赖包的源码进行修改进行使用&#xff0c;但我们又不能对已有的源码官网进行上传更新&#xff0c;这时&#xff0c;我们可以获取依赖包进行修改后&#xff0c;自行部署到https://npmjs.com中 1.官网https://npmjs.com中注册一个账号&#xff08;账号&…

微信小程序 editor图片上传到node服务器并展示在当前页面

前端 html <!-- 富文本 --><view class"container"><editor id"editor" ref"editor" :placeholderplaceholder input"onInput" ready"onEditorReady"></editor></view><view class…

pytorch 的matmult()函数详解

torch.matmul()也是一种类似于矩阵相乘操作的tensor连乘操作。但是它可以利用python中的广播机制&#xff0c;处理一些维度不同的tensor结构进行相乘操作。 matmul 就是矩阵求 叉乘 如果是二维矩阵&#xff0c;两个矩阵的大小应该为m*n &#xff0c;n*m。 一维向量的乘积&…

使用 .NET 开始 OpenAI Completions

作者&#xff1a;Luis Quintanilla 翻译&#xff1a;Alan Wang 排版&#xff1a;Alan Wang 欢迎来到有关 OpenAI 和 .NET 的博客系列&#xff01; 如果您是新来的&#xff0c;请查看我们的第一篇文章&#xff0c;我们在其中介绍了系列内容&#xff0c;并向您展示如何在 .NET 中…