线程间通信方式(互斥(互斥锁)与同步(无名信号量、条件变量))

1通信机制:互斥与同步

线程的互斥通过线程的互斥锁完成;

线程的同步通过无名信号量或者条件变量完成。

2  互斥

2.1 何为互斥?

        互斥是在多个线程在访问同一个全局变量的时候,先让这个线程争抢锁的资源,那个线程争抢到资源,它可以访问这个变量,没有争抢到资源的线程不能够访问这个变量。那这种只有一个线程能够访问到这个变量的现象称之为线程间互斥。

2.2互斥锁API

1.定义互斥锁
    pthread_mutex_t mutex;
2.初始化线程互斥锁
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
 //静态初始化


 int pthread_mutex_init(pthread_mutex_t * mutex,
                           const pthread_mutexattr_t * attr);
 //动态初始化
 功能:初始化互斥锁
    参数:
        @mutex:被初始化的锁
  @attr:锁的属性,一般填写为NULL(默认属性)
 返回值:成功返回0,失败返回错误码
3.上锁
     int pthread_mutex_trylock(pthread_mutex_t *mutex);
  //尝试获取锁,如果锁资源存在那就占用锁,如果锁资源不可利用,立即返回。
  int pthread_mutex_lock(pthread_mutex_t *mutex);
  功能:上锁(如果线程获取不到锁的资源,线程阻塞,直到其他的线程将锁释放)
     参数:
          @mutex:执行锁的指针
  返回值:成功返回0,失败返回错误码
4.解锁
 int pthread_mutex_unlock(pthread_mutex_t *mutex);
 功能:解锁
 参数:
          @mutex:执行锁的指针
  返回值:成功返回0,失败返回错误码 
5.销毁锁
 int pthread_mutex_destroy(pthread_mutex_t *mutex);
 功能:销毁互斥锁
    参数:
        @mutex:执行锁的指针
 返回值:成功返回0,失败返回错误码

实例

#include <head.h>
volatile int money = 1000;
pthread_mutex_t lock; // 定义线程互斥锁
void *thread1(void *arg)
{
    while (1)
    {
        pthread_mutex_lock(&lock); // 上锁
        money -= 50;
        if (money >= 0)
        {
            printf("张三取走了50块钱,余额 = %d\n", money);
        }
        else
        {
            money += 50;
            printf("张三取钱失败,余额不足...\n");
            pthread_mutex_unlock(&lock); // 解锁
            pthread_exit(NULL);
        }
        // sleep(1);
        pthread_mutex_unlock(&lock); // 解锁
    }
}
void *thread2(void *arg)
{
    while (1)
    {
        pthread_mutex_lock(&lock); // 上锁
        money -= 100;
        if (money >= 0)
        {
            printf("李四取走了100块钱,余额 = %d\n", money);
        }
        else
        {
            money += 100;
            printf("李四取钱失败,余额不足...\n");
            pthread_mutex_unlock(&lock); // 解锁
            pthread_exit(NULL);
        }
        // sleep(1);
        pthread_mutex_unlock(&lock); // 解锁
    }
}
int main(int argc, const char *argv[])
{
    pthread_t tid1, tid2; // typedef unsigned long int pthread_t;
    if ((errno = pthread_mutex_init(&lock, NULL)) != 0)
    { // 线程互斥锁初始化
        perror("pthread_mutex_init error");
        exit(-1);
    }
    if ((errno = pthread_create(&tid1, NULL, thread1, NULL)) != 0)
    {
        perror("pthread_create error");
        exit(-1);
    }
    if ((errno = pthread_create(&tid2, NULL, thread2, NULL)) != 0)
    {
        perror("pthread_create error");
        exit(-1);
    }
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    pthread_mutex_destroy(&lock);
    return 0;
}

 运行结果

注:使用锁不当可能会产生死锁,死锁规避方法

1.指定线程获取锁的状态

2.尽量避免锁的嵌套使用

3.给线程上锁指定超时时间

4.在全局位置指定锁是否被使用的状态,如果被使用,就不在获取锁(使用volatile int flag=0或1)

        3.同步

3.1 何为同步

线程同步机制是指线程的顺序执行,在线程执行前已经编排好了线程的执行顺序。就不会出现同一时间有多个现在在争抢临界资源了。线程的同步机制一般使用在生成者和消费者模型上(本身也是强调顺序)。

3.2  无名信号量API

注:无名信号量适合线程数比较少的情况的线程同步

#include <semaphore.h>
1.定义无名信号量
    sem_t sem;
2.初始化无名信号量
 int sem_init(sem_t *sem, int pshared, unsigned int value);
 功能:初始化无名信号量
    参数:
        @sem:指向无名信号量的指针
        @pshared:0 线程的同步
              1 进程的同步(亲缘关系进程)
        @value:信号的初值  1 0
 返回值:成功返回0,失败返回-1置位错误码
3.获取信号量(P操作)
    int sem_wait(sem_t *sem);
 功能:申请资源(让信号量的值减去1,然后和0比较如果结果为0,表示获取锁成功了)    
        如果在调用sem_wait的时候获取不到资源,sem_wait会阻塞
    参数:
        @sem:指向无名信号量的指针
 返回值:成功返回0,失败返回-1置位错误码
4.释放信号量(V操作)
    int sem_post(sem_t *sem);
 功能:释放资源
 参数:
        @sem:指向无名信号量的指针
 返回值:成功返回0,失败返回-1置位错误码
5.销毁无名信号量
 int sem_destroy(sem_t *sem);
 功能:销毁无名信号量
 参数:
        @sem:指向无名信号量的指针
 返回值:成功返回0,失败返回-1置位错误码

实例

要求:有三个线程A,B,C它们分别打印B、G、M三个字符,请使用无名信号量让这三个线程依次打印        BGM

            BGM

            BGM....

03_pthread_wumingxinhaoliang_lizi.c

#include <head.h>
sem_t sem1, sem2, sem3; // 定义无名信号量
void *thread1(void *arg)
{
    while (1)
    {
        sem_wait(&sem1);
        printf("E");
        sem_post(&sem2);
    }
}
void *thread2(void *arg)
{
    while (1)
    {
        sem_wait(&sem2);
        printf("G");
        sem_post(&sem3);
    }
}
void *thread3(void *arg)
{
    while (1)
    {
        sem_wait(&sem3);
        printf("M\n");
        sleep(1);
        sem_post(&sem1);
    }
}
int main(int argc, const char *argv[])
{
    pthread_t tid1, tid2, tid3;
    sem_init(&sem1, 0, 1); // 无名信号量初始化
    sem_init(&sem2, 0, 0);
    sem_init(&sem3, 0, 0);
    if ((errno = pthread_create(&tid1, NULL, thread1, NULL)) != 0)
    {
        perror("pthread create1 error");
        exit(-1);
    }
    if ((errno = pthread_create(&tid2, NULL, thread2,NULL)) != 0)
    {
        perror("pthread create2 error");
        exit(-1);
    }
    if ((errno = pthread_create(&tid3, NULL, thread3, NULL)) != 0)
    {
        perror("pthread create3 error");
        exit(-1);
    }

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    pthread_join(tid3, NULL);
    sem_destroy(&sem1); // 销毁无名信号量
    sem_destroy(&sem2);
    sem_destroy(&sem3);
    return 0;
}

执行gcc 03_pthread_wumingxinhaoliang_lizi.c -lpthread 编译

运行结果

3.3 条件变量API

        条件变量和无名信号量都是用于线程同步,用哪一个?

        无名信号量适合线程数比较少的情况的线程同步,而条件变量适合大量线程的同步工作。

1.定义条件变量
 pthread_cond_t cond;

2.初始化条件变量
    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
    //静态初始化 
    int pthread_cond_init(pthread_cond_t * cond,const pthread_condattr_t * attr);
    功能:动态初始化一个条件变量
    参数:
       @cond:条件变量的指针
       @attr:NULL使用默认属性
    返回值:成功返回0,失败返回非0

3.阻塞等待条件变量
    int pthread_cond_wait(pthread_cond_t * cond,pthread_mutex_t * mutex);
    功能:阻塞等待条件变量,在条件变量中维护了一个队列,这里的互斥锁就是为
        了解决在往队列中放线程的时候出现竞态问题的。
    使用的步骤:
        1.使用pthread_mutex_lock上锁
        2.调用pthread_cond_wait
            2.1将当前线程放入队列
            2.2解锁
            2.3休眠
            2.4获取锁(PS:此时是为了防止出入队列冲突 假设就剩一个元素,是先进还是先出 要争抢一个锁才行) 
            2.5休眠状态退出
        3.你的程序
        4.使用pthread_mutex_unlock解锁
    参数:
        @cond:条件变量的地址
        @mutex:互斥锁
    返回值:成功返回0,失败返回非零
        
     
4.给休眠的线程发信号或者广播
    int pthread_cond_signal(pthread_cond_t *cond);
    功能:唤醒(至少)一个休眠的线程
    参数:
        @cond:条件变量的地址
    返回值:成功返回0,失败返回非零
    int pthread_cond_broadcast(pthread_cond_t *cond);
    功能:唤醒所有休眠的线程
    参数:
        @cond:条件变量的地址
    返回值:成功返回0,失败返回非零     
       
5.销毁条件变量     
    int pthread_cond_destroy(pthread_cond_t *cond);
    功能:销毁条件变量
    参数:
         @cond:条件变量的地址
    返回值:成功返回0,失败返回非零 ,

实例:

一个生产者线程多个消费者线程(同步)

#include <head.h>
pthread_mutex_t lock; // 定义互斥锁
pthread_cond_t cond;  // 定义条件变量
void *thread1(void *arg)
{
    while(1){
        sleep(1);//sleep(1)一下 调用thread2的线程全部进入休眠了 
        printf("我生产了一部手机..\n");
        pthread_cond_signal(&cond);
        // pthread_cond_broadcast(&cond);
    }
}
void *thread2(void *arg)
{
    while(1){
        pthread_mutex_lock(&lock);
        pthread_cond_wait(&cond,&lock);
        printf("%#lx:购买了一部手机\n",pthread_self());
        pthread_mutex_unlock(&lock);
    }
}

int main(int argc, const char *argv[])
{
    pthread_t tid1, tid2, tid3, tid4, tid5;
    pthread_mutex_init(&lock, NULL); // 初始化锁
    pthread_cond_init(&cond, NULL);  // 初始化条件变量
    if ((errno = pthread_create(&tid1, NULL, thread1, NULL)) != 0)
    {
        perror("pthread create1 error");
        exit(-1);
    }
    if ((errno = pthread_create(&tid2, NULL, thread2, NULL)) != 0)
    {
        perror("pthread create2 error");
        exit(-1);
    }
    if ((errno = pthread_create(&tid3, NULL, thread2, NULL)) != 0)
    {
        perror("pthread create3 error");
        exit(-1);
    }
    if ((errno = pthread_create(&tid4, NULL, thread2, NULL)) != 0)
    {
        perror("pthread create4 error");
        exit(-1);
    }
    if ((errno = pthread_create(&tid5, NULL, thread2, NULL)) != 0)
    {
        perror("pthread create5 error");
        exit(-1);
    }
    printf("tid1 = %#lx,tid2 = %#lx,tid3 = %#lx,tid4 = %#lx,tid5 = %#lx\n", tid1, tid2, tid3, tid4, tid5);
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    pthread_join(tid3, NULL);
    pthread_join(tid4, NULL);
    pthread_join(tid5, NULL);

    return 0;
}

运行结果

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

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

相关文章

学会python——对目录的操作(python实例十)

目录 1、认识Python 2、环境与工具 2.1 python环境 2.2 Visual Studio Code编译 3、遍历当前目录 3.1 代码构思 3.2 代码示例 3.3 运行结果 4、删除目录中的文件 4.1 代码构思 4.2 代码示例 4.3 运行结果 5、总计 1、认识Python Python 是一个高层次的结合了解释性…

Linux-安装及管理程序

目录 一、Linux应用程序基础 1、应用程序与系统命令的关系 2、 典型应用程序的目录结构 3、常见的软件包封装类型 二、RPM包管理工具 1、RPM包管理器 2、RPM软件包 ​3、RPM的命令格式 4、RPM命令的常用选项 5、RPM安装 三、 yum安装 1、yum源介绍 1.1、本地yum源 …

ClosedXML

一、类库介绍 ClosedXML是一个用于读取、操作和写入Excel 2007 (.xlsx, .xlsm)文件的.NET第三方库。它基于OpenXML&#xff0c;但与OpenXML相比&#xff0c;ClosedXML具有更高的性能和更易于使用的API接口。 ClosedXML支持XML文档的解析和生成&#xff0c;可以处理复杂的XML结…

程序员如何高效读代码?

程序员高效读代码的技巧包括以下几点&#xff1a; 明确阅读目的&#xff1a;在开始阅读代码之前&#xff0c;先明确你的阅读目的。是为了理解整个系统的架构&#xff1f;还是为了修复一个具体的bug&#xff1f;或者是为了了解某个功能是如何实现的&#xff1f;明确目的可以帮助…

系统安全设计规范(Word原件)

1.1安全建设原则 1.2 安全管理体系 1.3 安全管理规范 1.4 数据安全保障措施 1.4.1 数据库安全保障 1.4.2 操作系统安全保障 1.4.3 病毒防治 1.5安全保障措施 1.5.1实名认证保障 1.5.2 接口安全保障 1.5.3 加密传输保障 1.5.4终端安全保障 资料获取&#xff1a;私信或者进主页。…

示例:WPF中推荐一个支持折叠展开的GridSpliter自定义控件GridSplitterBox

一、目的&#xff1a;推荐一个支持折叠展开的GridSpliter自定义控件GridSplitterBox 二、效果 实现功能&#xff1a;设置菜单显示位置&#xff0c;最小宽度&#xff0c;最大宽度&#xff0c;位置持久化保存 三、环境 VS2022 Net7 四、使用方式 1、安装nuget包&#xff1a;H…

能理解你的意图的自动化采集工具——AI和爬虫相结合

⭐️我叫忆_恒心&#xff0c;一名喜欢书写博客的研究生&#x1f468;‍&#x1f393;。 如果觉得本文能帮到您&#xff0c;麻烦点个赞&#x1f44d;呗&#xff01; 近期会不断在专栏里进行更新讲解博客~~~ 有什么问题的小伙伴 欢迎留言提问欧&#xff0c;喜欢的小伙伴给个三连支…

pytest测试框架flaky插件重试失败用例

Pytest提供了丰富的插件来扩展其功能&#xff0c;本章介绍下插件flaky &#xff0c;用于在测试用例失败时自动重新运行这些测试用例。与前面文章介绍的插件pytest-rerunfailures功能有些类似&#xff0c;但是功能上不如pytest-rerunfailures插件丰富。 flaky官方并没有明确pyt…

中国企业数字化转型现状、趋势和挑战

一、来自不同行业、不同所有制的145家企业的调查 为了了解中国企业数字化转型的现状、趋势和挑战&#xff0c;2022年我们完成了一次在线问卷调查。 受访企业达145家&#xff0c;国内企业111家&#xff0c;占比77%&#xff08;其中央企占总比例51%&#xff09;&#xff0c;民营…

【Python机器学习】k均值聚类——矢量量化,或者将k均值看作分解

虽然k均值是一种聚类算法&#xff0c;但在k均值和分解方法之间存在一些相似之处。k均值尝试利用簇中心来表示每个数据点&#xff0c;可以看作仅用一个分量来表示每个数据点&#xff0c;该分量由簇中心给出。这种观点将k均值看作是一种分解方法&#xff0c;其中每个点用单一分量…

【计算机组成原理】部分题目汇总

计算机组成原理 部分题目汇总 一. 简答题 RISC和CICS 简要说明&#xff0c;比较异同 RISC&#xff08;精简指令集&#xff09;注重简单快速的指令执行&#xff0c;使用少量通用寄存器&#xff0c;固定长度指令&#xff0c;优化硬件性能&#xff0c;依赖软件&#xff08;如编译…

Adobe Acrobat 编辑器软件下载安装,Acrobat 轻松编辑和管理各种PDF文件

Adobe Acrobat&#xff0c;它凭借卓越的功能和丰富的工具&#xff0c;为用户提供了一个全面的解决方案&#xff0c;用于查看、创建、编辑和管理各种PDF文件。 作为一款专业的PDF阅读器&#xff0c;Adobe Acrobat能够轻松打开并展示各种格式的PDF文档&#xff0c;无论是文字、图…

软考初级网络管理员__软件单选题

1.在Excel 中&#xff0c;设单元格F1的值为56.323&#xff0c;若在单元格F2中输入公式"TEXT(F1,"&#xffe5;0.00”)”&#xff0c;则单元格F2的值为()。 &#xffe5;56 &#xffe5;56.323 &#xffe5;56.32 &#xffe5;56.00 2.要使Word 能自动提醒英文单…

前沿技术丨S2S自动化测试解决方案

技术背景 随着面向服务的架构&#xff08;Service-Oriented Architecture&#xff0c;SOA&#xff09;在整车架构中的逐步推进及应用&#xff0c;车内网络通信中会一直并存基于以太网的面向服务和基于传统网络的面向信号的两类控制器&#xff0c;S2S&#xff08;Signal to Ser…

USB - USB在消费领域的应用

Switching in USB Consumer Applications 通用串行总线&#xff08;USB&#xff09;已成为满足终端设备之间日益增长的快速数据传输需求的主流接口--例如&#xff0c;在个人电脑和便携式设备&#xff08;如手机、数码相机和个人媒体播放器&#xff09;之间下载和上传数据。 The…

如何与情绪好好相处,真正成为情绪的主人

一、教程描述 若要成为一个聪明的人&#xff0c;就要学会做情绪的主人&#xff0c;而不是被情绪控制自己&#xff0c;为什么要做情绪的主人&#xff1f;至少有以下两个方面原因。 其一&#xff0c;都说&#xff0c;世上还是好人多。可是&#xff0c;为什么你身边没有一个好人…

npm 安装踩坑

1 网络正常&#xff0c;但是以前的老项目安装依赖一直卡住无法安装&#xff1f;哪怕切换成淘宝镜像 解决办法&#xff1a;切换成yarn (1) npm i yarn -g(2) yarn init(3) yarn install在安装的过程中发现&#xff1a; [2/4] Fetching packages... error marked11.1.0:…

android 彩虹进度条自定义view实现

实现一个彩虹色进度条功能&#xff0c;不说明具体用途大家应该能猜到。想找别人造的轮子&#xff0c;但是没有合适的&#xff0c;所以决定自己实现一个。 相关知识 android 自定义view LinearGradient 线性渐变 实现步骤 自定义view 自定义一个TmcView类继承View 重写两…

单体服务系统认证

上一节讲了如何使用JWT生成令牌&#xff0c;下面说说单体服务认证基本流程。 认证流程 流程图&#xff1a; 流程描述&#xff1a; 用户输入登录信息&#xff0c;客户端&#xff08;Web/APP等&#xff09;发起登录请求&#xff1b;服务端校验该用户是否有效&#xff0c;用户…

批量重命名神器揭秘:一键实现文件夹随机命名,自定义长度轻松搞定!

在数字化时代&#xff0c;我们经常需要管理大量的文件夹&#xff0c;尤其是对于那些需要频繁更改或整理的文件来说&#xff0c;给它们进行批量重命名可以大大提高工作效率。然而&#xff0c;传统的重命名方法既繁琐又耗时&#xff0c;无法满足高效工作的需求。今天&#xff0c;…