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

news2024/10/6 18:33:44

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;
}

运行结果

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

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

相关文章

理解广角镜头的视野和畸变

为什么广角镜头的视野会比长焦镜头的视野大呢&#xff1f; 我之前用等光程解释了景深&#xff0c;也解释了为什么焦距越远&#xff0c;成像越大&#xff0c;但是从来没有提到过视野范围这个概念。实际上在我之前建立的数学模型中&#xff0c;物曲面S是无限大的&#xff0c;像曲…

python:画圆圈,绘制生命之花图案

参阅&#xff1a;生命之花/平衡轮&#xff1a;一个实用的人生规划的工具 先画一个圆圈&#xff0c;编写 draw_circle1.py 如下 # -*- coding: utf-8 -*- """ 画一个圆圈 """ import numpy as np from matplotlib import pyplot as plt# 用于…

(done) 关于 GNU/Linux API setenv 的实验

写一个下面的代码来验证 #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <sys/types.h>int main() {// 设置环境变量 MY_VAR 的值为 "hello_world"if (setenv("MY_VAR", "hello_world", 1) ! 0…

动手学深度学习(Pytorch版)代码实践 -卷积神经网络-29残差网络ResNet

29残差网络ResNet import torch from torch import nn from torch.nn import functional as F import liliPytorch as lp import matplotlib.pyplot as plt# 定义一个继承自nn.Module的残差块类 class Residual(nn.Module):def __init__(self, input_channels, num_chan…

adb 查看哪些应用是双开的

adb shell pm list users 得到 这 里有 user 0 ,11,999 其中0是系统默认的&#xff0c;11是平行空间的&#xff0c;999是双开用户 pm list packages --user 999 -3 得到了999用户安装第三方应用的包名 pm list packages --user 11 -3 得到了隐私空间用户安装第三方应用的…

Git客户端安装步骤详解

git windows7 百度经验:jingyan.baidu.com 方法/步骤 1 从git官网下一个git安装包。 步骤阅读 2 点击git.exe安装程序&#xff0c;点击【next】 ![git的安装和配置](https://imgsa.baidu.com/exp/w500/sign7565f44ba58b87d65042ab1f37092860/21a4462309f790525e5b0144…

兰州理工大学24计算机考研情况,好多专业都接受调剂,只有计算机专硕不接收调剂,复试线为283分!

兰州理工大学&#xff08;Lanzhou University of Technology&#xff09;&#xff0c;位于甘肃省兰州市&#xff0c;是甘肃省人民政府、教育部、国家国防科技工业局共建高校&#xff0c;甘肃省高水平大学和“一流学科”建设高校&#xff1b;入选国家“中西部高校基础能力建设工…

Android-系统开发_四大组件篇----探讨-Activity-的生命周期

当一个活动不再处于栈顶位置&#xff0c;但仍然可见时&#xff0c;这时活动就进入了暂停状态。你可能会觉得既然活动已经不在栈顶了&#xff0c;还怎么会可见呢&#xff1f; 这是因为并不是每一个活动都会占满整个屏幕&#xff0c;比如对话框形式的活动只会占用屏幕中间的部分…

Retrieval-Augmented Generation for Large Language Models A Survey

Retrieval-Augmented Generation for Large Language Models: A Survey 文献综述 文章目录 Retrieval-Augmented Generation for Large Language Models: A Survey 文献综述 Abstract背景介绍 RAG概述原始RAG先进RAG预检索过程后检索过程 模块化RAGModules部分Patterns部分 RAG…

15天搭建ETF量化交易系统Day9—玩大A必学网格策略

搭建过程 每个交易者都应该形成一套自己的交易系统。 很多交易者也清楚知道&#xff0c;搭建自己交易系统的重要性。现实中&#xff0c;从&#xff10;到&#xff11;往往是最难跨越的一步。 授人鱼不如授人以渔&#xff0c;为了帮助大家跨出搭建量化系统的第一步&#xff0c;我…

vscode用vue框架2,续写登陆页面逻辑,以及首页框架的搭建

目录 前言&#xff1a; 一、实现登录页信息验证逻辑 1.实现登录数据双向绑定 2.验证用户输入数据是否和默认数据相同 补充知识1&#xff1a; 知识点补充2&#xff1a; 二、首页和登录页之间的逻辑(1) 1. 修改路由&#xff0c;使得程序被访问先访问首页 知识点补充3&am…

[每周一更]-(第102期):认识相机格式Exif

文章目录 EXIF数据包含的信息读取EXIF数据的工具和库EXIF数据读取示例&#xff08;Go语言&#xff09;想法参考 相机拍摄的照片&#xff0c;在照片展示行无水印信息&#xff0c;但是照片属性中会包含比较丰富的信息&#xff0c;相机品牌、型号、镜头信息等&#xff0c;这些我们…

基于NURBS曲线的数据拟合算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 4.1NURBS曲线基础 4.2 数据拟合原理 5.完整程序 1.程序功能描述 基于NURBS曲线的数据拟合算法,非均匀有理B样条&#xff08;Non-Uniform Rational B-Splines&#xff0c;简称NURBS&#xf…

电子商务的未来:大数据||论主流电商大数据采集API接口的重要性

每天全球生成2.5万亿字节。金融交易&#xff0c;用户出版物和众多内容&#xff0c;数据对于组织了解消费者的行为和实际需求极为重要。 在一个互联、动态和竞争激烈的多元世界中&#xff0c;能够预测技术和市场变化的组织在竞争中生存下来。这是大数据管理的真正目标&#xff0…

鸿蒙生态伙伴SDK市场正式发布,驱动千行百业鸿蒙原生应用开发

6月21-23日&#xff0c;华为开发者大会&#xff08;HDC 2024&#xff09;在东莞举办。在22日举办的【鸿蒙生态伙伴SDK】论坛中&#xff0c;正式发布了【鸿蒙生态伙伴SDK市场】&#xff08;以下简称&#xff1a;伙伴SDK市场&#xff09;&#xff0c;伙伴SDK市场是为开发者提供各…

PHP转Go系列 | ThinkPHP与Gin的使用姿势

大家好&#xff0c;我是码农先森。 安装 使用 composer 进行项目的创建。 composer create-project topthink/think thinkphp_demo使用 go mod 初始化项目。 go mod init gin_demo目录 thinkphp_demo 项目目录结构。 thinkphp_demo ├── LICENSE.txt ├── README.md …

JVM专题三:Java代码如何运行

通过前面的第一篇文章&#xff0c;对JVM整体脉络有了一个大概了解。第二篇文章我们通过对高级语言低级语言不同特性的探讨引出了Java的编译过程。有了前面的铺垫&#xff0c;咱们今天正式进入Java到底是如何运行起来的探讨。 目前大部分公司都是使用maven作为包管理工具&#x…

Flink-03 Flink Java 3分钟上手 Stream 给 Flink-02 DataStreamSource Socket写一个测试的工具!

代码仓库 会同步代码到 GitHub https://github.com/turbo-duck/flink-demo 当前章节 继续上一节的内容&#xff1a;https://blog.csdn.net/w776341482/article/details/139875037 上一节中&#xff0c;我们需要使用 nc 或者 telnet 等工具来模拟 Socket 流。这节我们写一个 …

基于YOLOv5的火灾检测系统的设计与实现(PyQT页面+YOLOv5模型+数据集)

基于YOLOv5的火灾检测系统的设计与实现 概述系统架构主要组件代码结构功能描述YOLOv5检测器视频处理器主窗口详细代码说明YOLOv5检测器类视频处理类主窗口类使用说明环境配置运行程序操作步骤检测示例图像检测视频检测实时检测数据集介绍数据集获取数据集规模YOLOv5模型介绍YOL…

python输入、输出和变量

一、变量 变量是存储数据的容器。在 Python 中&#xff0c;变量在使用前不需要声明数据类型&#xff0c;Python 会根据赋值自动推断变量类型。 定义变量&#xff1a; 二、输入&#xff08;Input&#xff09; input() 函数用于获取用户输入。默认情况下&#xff0c;input() 会…