首页 > 其他分享 >线程间通信方式

线程间通信方式

时间:2024-06-23 17:57:32浏览次数:3  
标签:方式 cond 间通信 mutex pthread 线程 sem NULL

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

运行结果

标签:方式,cond,间通信,mutex,pthread,线程,sem,NULL
From: https://blog.csdn.net/CSDN_DU666666/article/details/139896966

相关文章

  • Windows Api如何创建一个快捷方式并且在开始菜单搜索到自己的应用
     原文链接:http://cshelloworld.com/home/detail/1804473083243925504当我们点击win10系统搜索框的时候,输入名称,win10会帮助我们匹配到对应的应用。这里搜索框实际上就是windows系统的开始菜单。接下来我们随便找一个应用,右键,然后点击打开文件位置,我们来看下这个EveryThing的......
  • 【2024最新精简版】线程安全/多线程 面试篇
    文章目录一.线程基础线程和进程什么是进程什么是线程并发与并行的区别创建线程继承Thread类实现Runable接口实现Callable接口使用线程池线程状态等待唤醒机制等待方法唤醒方法二.线程池线程池作用创建线程池线程池任务调度流程阻塞队列BlockingQueue线程池拒绝策......
  • 掌握Perl并发:线程与进程编程全攻略
    掌握Perl并发:线程与进程编程全攻略引言Perl作为一种功能强大的编程语言,提供了丰富的并发编程手段。无论是通过threads模块实现的线程,还是通过fork系统调用产生的进程,Perl都能帮助开发者高效地处理多任务。本文将深入探讨如何在Perl中使用线程和进程,带领读者掌握并发编程的......
  • 深探Java线程池协同神器——CountDownLatch的源码奥秘与实战应用
    1.概述CountDownLatch,作为Java并发包java.util.concurrent下的重要一员,其设计理念在于提供一个线程同步工具,允许一个或多个线程等待其他线程完成操作后再继续执行。在工程师的眼中,它不仅是多线程编程中的一把利器,更是实现线程间高效协同的关键所在。2.源码分析构造函......
  • OpenWrt 无法通过 ssh 免密码方式访问 git 服务器的原因及解决方案
    问题原因openssh-keygen和openssh-client非OpenWrt默认安装的包ssh-keygen生成的私钥PRIVATEKEY不是600权限,而是644权限,权限太开放会导致SSH拒绝使用它解决方案安装相关包opkgupdateopkginstallopenssh-client openssh-keygen生成密钥对ssh-keygen......
  • 申请SSL证书保姆级教程,包括FreeSSL申请、Acme脚本申请等方式。
    Acme脚本申请证书Acme脚本申请证书,是我们用到的最常见的一种证书的申请方式,它有很多的申请方法,大家只需要找到一种适合自己的也就好了。不管用下面的何种方式申请,都需要安装Acme,有一部分的申请场景需要用到相关的插件,所以我们需要提前安装。下面环境的安装方式,大家根据自己......
  • SpringBoot + 虚拟线程,鸟枪换大炮!
    “虚拟”线程,望文生义,它是“假”的,它不直接调度操作系统的线程,而是由JVM再提供一层线程的接口抽象,由普通线程调度,即一个普通的操作系统线程可以调度成千上万个虚拟线程。虚拟线程比普通线程的消耗要小得多得多,在内存足够的情况下,我们甚至可以创建上百万的虚拟线程,这在之前(Jav......
  • 面试八股之线程篇2.5——线程中的并发锁篇——AQS
    ......
  • 密码、手机等隐私信息的保存方式
    将用户密码以密文形式存储进数据库是保护用户信息安全的重要措施。以下是一些常见的密码加密存储方案:哈希加密:使用哈希函数(如SHA-256,SHA-1,MD5等)将密码转换为固定长度的哈希值。SHA-256是目前较为安全的哈希算法,它生成的哈希值长度为64个字符,极大地提高了安全性。需要注......
  • 【C#进阶】多线程和异步编程_2024-06-22
    关于多线程和异步编程简单来说,就是多线程并行执行任务提速,异步编程等待不浪费资源,并发集合确保数据访问安全,三者合力提升程序效率与反应能力。1.理解线程想象一下,你在厨房做饭,同时需要洗菜、切菜、炒菜。如果你一个人来做,就需要在这些任务之间来回切换,这很慢。但如果请几个朋友......