线程同步--条件变量,信号量

news2024/11/28 5:36:04

生产者和消费者模型

image-20240512142852414

案例
/*
    生产者消费者模型(粗略的版本)
*/
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>

// 创建一个互斥量
pthread_mutex_t mutex;

struct Node{
    int num;
    struct Node *next;
};

// 头结点
struct Node * head = NULL;

void * producer(void * arg) {

    // 不断的创建新的节点,添加到链表中,头插法
    while(1) {
        pthread_mutex_lock(&mutex);
        struct Node * newNode = (struct Node *)malloc(sizeof(struct Node));
        newNode->next = head;
        head = newNode;
        newNode->num = rand() % 1000;
        printf("add node, num : %d, tid : %ld\n", newNode->num, pthread_self());
        pthread_mutex_unlock(&mutex);
        usleep(100);
    }

    return NULL;
}

void * customer(void * arg) {

    while(1) {
        pthread_mutex_lock(&mutex);
        // 保存头结点的指针
        struct Node * tmp = head;

        // 判断是否有数据
        if(head != NULL) {
            // 有数据
            head = head->next;
            printf("del node, num : %d, tid : %ld\n", tmp->num, pthread_self());
            free(tmp);
            pthread_mutex_unlock(&mutex);
            usleep(100);
        } else {
            // 没有数据
            pthread_mutex_unlock(&mutex);
        }
    }
    return  NULL;
}

int main() {

    pthread_mutex_init(&mutex, NULL);

    // 创建5个生产者线程,和5个消费者线程
    pthread_t ptids[5], ctids[5];

    for(int i = 0; i < 5; i++) {
        pthread_create(&ptids[i], NULL, producer, NULL);
        pthread_create(&ctids[i], NULL, customer, NULL);
    }

    for(int i = 0; i < 5; i++) {
        pthread_detach(ptids[i]);
        pthread_detach(ctids[i]);
    }

    while(1) {
        sleep(10);
    }

    pthread_mutex_destroy(&mutex);

    pthread_exit(NULL);

    return 0;
}

条件变量

条件变量不是锁,可以引起线程阻塞,某个条件满足以后阻塞线程或解除线程,他不能解决数据混乱的问题

条件变量的类型 pthread_cond_t

这些函数是 POSIX 线程(pthread)库的一部分,用于在 C 和 C++ 程序中提供线程处理能力。这里讨论的特定函数与条件变量有关,条件变量是同步原语,允许线程挂起执行并等待某些条件发生。

条件变量相关函数
1. pthread_cond_init

此函数初始化条件变量。它接受两个参数:

  • cond:指向要初始化的条件变量的指针。
  • attr:指向 pthread_condattr_t 结构的指针,该结构指定条件变量的任何属性。如果使用默认属性,此参数可以为 NULL。
pthread_cond_t cond;
pthread_cond_init(&cond, NULL);
2. pthread_cond_destroy

此函数销毁条件变量,释放与之关联的任何资源。在不再需要已初始化的条件变量时应调用它,以避免内存泄漏。

  • cond:指向要销毁的条件变量的指针。
pthread_cond_destroy(&cond);
3. pthread_cond_wait

此函数会阻塞调用线程,直到指定条件被触发。调用时必须由调用线程锁定互斥锁;pthread_cond_wait 会自动释放互斥锁并使调用线程在条件变量上阻塞。

  • cond:指向要等待的条件变量的指针。
  • mutex:指向互斥锁的指针。
pthread_cond_wait(&cond, &mutex);
4. pthread_cond_timedwait

pthread_cond_wait 类似,但带有超时。调用线程被阻塞,直到指定条件被触发或超时发生。

  • cond:指向条件变量的指针。
  • mutex:指向互斥锁的指针。
  • abstime:指向 timespec 结构的指针,指定绝对超时时间。
struct timespec ts;
// 设置 ts 为某个绝对时间
pthread_cond_timedwait(&cond, &mutex, &ts);
5. pthread_cond_signal

此函数至少解除阻塞在指定条件变量上的一个线程。它不释放互斥锁,也不会将互斥锁从信号线程转移给被唤醒的线程。

  • cond:指向要发信号的条件变量的指针。
pthread_cond_signal(&cond);
6. pthread_cond_broadcast

此函数解除阻塞所有当前在指定条件变量上阻塞的线程。与 pthread_cond_signal 不同,后者唤醒至少一个等待线程,broadcast 唤醒所有等待线程。

  • cond:指向要广播的条件变量的指针。
pthread_cond_broadcast(&cond);

这些函数是多线程编程中管理共享资源和同步不同线程执行流的重要工具。有效使用它们可以帮助避免复杂的多线程应用中的竞态条件和死锁。

案例
/*
    条件变量的类型 pthread_cond_t
    int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
    int pthread_cond_destroy(pthread_cond_t *cond);
    int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
        - 等待,调用了该函数,线程会阻塞。
    int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
        - 等待多长时间,调用了这个函数,线程会阻塞,直到指定的时间结束。
    int pthread_cond_signal(pthread_cond_t *cond);
        - 唤醒一个或者多个等待的线程
    int pthread_cond_broadcast(pthread_cond_t *cond);
        - 唤醒所有的等待的线程
*/
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>

// 创建一个互斥量
pthread_mutex_t mutex;
// 创建条件变量
pthread_cond_t cond;

struct Node{
    int num;
    struct Node *next;
};

// 头结点
struct Node * head = NULL;

void * producer(void * arg) {

    // 不断的创建新的节点,添加到链表中
    while(1) {
        pthread_mutex_lock(&mutex);
        struct Node * newNode = (struct Node *)malloc(sizeof(struct Node));
        newNode->next = head;
        head = newNode;
        newNode->num = rand() % 1000;
        printf("add node, num : %d, tid : %ld\n", newNode->num, pthread_self());
        
        // 只要生产了一个,就通知消费者消费
        pthread_cond_signal(&cond);

        pthread_mutex_unlock(&mutex);
        usleep(100);
    }

    return NULL;
}

void * customer(void * arg) {

    while(1) {
        pthread_mutex_lock(&mutex);
        // 保存头结点的指针
        struct Node * tmp = head;
        // 判断是否有数据
        if(head != NULL) {
            // 有数据
            head = head->next;
            printf("del node, num : %d, tid : %ld\n", tmp->num, pthread_self());
            free(tmp);
            pthread_mutex_unlock(&mutex);
            usleep(100);
        } else {
            // 没有数据,需要等待
            // 当这个函数调用阻塞的时候,会对互斥锁进行解锁,当不阻塞的,继续向下执行,会重新加锁。
            pthread_cond_wait(&cond, &mutex);
            pthread_mutex_unlock(&mutex);
        }
    }
    return  NULL;
}

int main() {

    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);

    // 创建5个生产者线程,和5个消费者线程
    pthread_t ptids[5], ctids[5];

    for(int i = 0; i < 5; i++) {
        pthread_create(&ptids[i], NULL, producer, NULL);
        pthread_create(&ctids[i], NULL, customer, NULL);
    }

    for(int i = 0; i < 5; i++) {
        pthread_detach(ptids[i]);
        pthread_detach(ctids[i]);
    }

    while(1) {
        sleep(10);
    }

    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);

    pthread_exit(NULL);

    return 0;
}

信号量

信号量的类型 sem_t

sem_init

sem_init(sem_t *sem, int pshared, unsigned int value): 初始化一个未命名的信号量。参数sem是指向信号量结构的指针,pshared指示信号量是在进程间共享(非0值)还是线程间共享(0值),value是信号量的初始值。

sem_destroy

sem_destroy(sem_t *sem): 销毁一个未命名的信号量,释放它可能占用的资源。参数sem是指向信号量结构的指针。

sem_wait

sem_wait(sem_t *sem): 等待信号量。如果信号量的值大于0,它将减少1并立即返回。如果信号量的值为0,调用线程将阻塞,直到信号量值变为非0。

sem_trywait

sem_trywait(sem_t *sem): 尝试等待信号量。它是sem_wait的非阻塞版本,如果信号量的值为0,则立即返回错误,而不是阻塞。

sem_timedwait

sem_timedwait(sem_t *sem, const struct timespec *abs_timeout): 在指定的超时时间内等待信号量。如果信号量在给定时间内变为可用,它将减少信号量的值并返回;如果超时,则返回错误。

sem_post

sem_post(sem_t *sem): 通过增加信号量的值来释放信号量,如果有线程因等待这个信号量而阻塞,它将被唤醒。

sem_getvalue

sem_getvalue(sem_t *sem, int *sval): 获取信号量的当前值。参数sval是一个指针,用于存储读取的信号量值。

案例

/*
    信号量的类型 sem_t
    int sem_init(sem_t *sem, int pshared, unsigned int value);
        - 初始化信号量
        - 参数:
            - sem : 信号量变量的地址
            - pshared : 0 用在线程间 ,非0 用在进程间
            - value : 信号量中的值

    int sem_destroy(sem_t *sem);
        - 释放资源

    int sem_wait(sem_t *sem);
        - 对信号量加锁,调用一次对信号量的值-1,如果值为0,就阻塞

    int sem_trywait(sem_t *sem);

    int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
    int sem_post(sem_t *sem);
        - 对信号量解锁,调用一次对信号量的值+1

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

    sem_t psem;
    sem_t csem;
    init(psem, 0, 8);
    init(csem, 0, 0);

    producer() {
        sem_wait(&psem);
        sem_post(&csem)
    }

    customer() {
        sem_wait(&csem);
        sem_post(&psem)
    }

*/

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

// 创建一个互斥量
pthread_mutex_t mutex;
// 创建两个信号量
sem_t psem;
sem_t csem;

struct Node{
    int num;
    struct Node *next;
};

// 头结点
struct Node * head = NULL;

void * producer(void * arg) {

    // 不断的创建新的节点,添加到链表中
    while(1) {
        sem_wait(&psem);
        pthread_mutex_lock(&mutex);
        struct Node * newNode = (struct Node *)malloc(sizeof(struct Node));
        newNode->next = head;
        head = newNode;
        newNode->num = rand() % 1000;
        printf("add node, num : %d, tid : %ld\n", newNode->num, pthread_self());
        pthread_mutex_unlock(&mutex);
        sem_post(&csem);
    }

    return NULL;
}

void * customer(void * arg) {

    while(1) {
        sem_wait(&csem);
        pthread_mutex_lock(&mutex);
        // 保存头结点的指针
        struct Node * tmp = head;
        head = head->next;
        printf("del node, num : %d, tid : %ld\n", tmp->num, pthread_self());
        free(tmp);
        pthread_mutex_unlock(&mutex);
        sem_post(&psem);
       
    }
    return  NULL;
}

int main() {

    pthread_mutex_init(&mutex, NULL);
    sem_init(&psem, 0, 8);
    sem_init(&csem, 0, 0);

    // 创建5个生产者线程,和5个消费者线程
    pthread_t ptids[5], ctids[5];

    for(int i = 0; i < 5; i++) {
        pthread_create(&ptids[i], NULL, producer, NULL);
        pthread_create(&ctids[i], NULL, customer, NULL);
    }

    for(int i = 0; i < 5; i++) {
        pthread_detach(ptids[i]);
        pthread_detach(ctids[i]);
    }

    while(1) {
        sleep(10);
    }

    pthread_mutex_destroy(&mutex);

    pthread_exit(NULL);

    return 0;
}

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

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

相关文章

【ALM】ALM解决方案系列:质量保证

1软件的质量管理现状与痛点 在软件开发中&#xff0c;质量被视为软件产品的生命。保证软件质量&#xff0c;是贯穿整个软件生命周期的重要问题。如果在软件开发早期忽视质量管理&#xff0c;会导致软件项目管理出现问题。因此&#xff0c;重视并规范软件管理在软件项目管理中起…

[论文笔记]Corrective Retrieval Augmented Generation

引言 今天带来论文Corrective Retrieval Augmented Generation的笔记&#xff0c;这是一篇优化RAG的工作。 大型语言模型(LLMs) inevitable(不可避免)会出现幻觉&#xff0c;因为生成的文本的准确性不能仅仅由其参数化知识来确保。尽管检索增强生成(RAG)是LLMs的一个可行补充…

带你手撕红黑树! c++实现 带源码

目录 一、概念 二、特性 三、接口实现 1、插入 情况一&#xff1a;p为黑&#xff0c;结束 情况二&#xff1a;p为红 1&#xff09;叔叔存在且为红色 2&#xff09;u不存在/u存在且为黑色 &#xff08;1&#xff09;p在左&#xff0c;u在右 &#xff08;2&#xff09;…

【半夜学习MySQL】表的约束(含主键、唯一键、外键、zerofill、列描述、默认值、空属性详解)

&#x1f3e0;关于专栏&#xff1a;半夜学习MySQL专栏用于记录MySQL数据相关内容。 &#x1f3af;每天努力一点点&#xff0c;技术变化看得见 文章目录 前言空属性默认值列描述zerofill主键主键概述主键删除与追加复合主键 自增长唯一键外键综合案例 前言 上一篇文章中介绍了数…

Vue3项目打包部署到云服务器的Nginx中

文章目录 一、打包vue3项目二、dist文件夹上传到服务器三、改nginx配置文件Docker安装nginx 一、打包vue3项目 npm run build 二、dist文件夹上传到服务器 将dist文件夹放到docker安装的nginx中的html目录下 三、改nginx配置文件 然后重启nginx【改了配置文件重启nginx才能…

JVM的原理与性能

1 JVM 内存结构 1.1 运行时数据区 1.1.1 栈&#xff08;虚拟机栈&#xff09; 每个线程在创建时都会创建一个私有的Java虚拟机栈&#xff0c;在执行每个方法时都会打包成一个栈帧&#xff0c;存储了局部变量表、操作数栈、动态链接、方法出口等信息&#xff0c;然后放入栈中。…

关于我转生从零开始学C++这件事:获得神器

❀❀❀ 文章由不准备秃的大伟原创 ❀❀❀ ♪♪♪ 若有转载&#xff0c;请联系博主哦~ ♪♪♪ ❤❤❤ 致力学好编程的宝藏博主&#xff0c;代码兴国&#xff01;❤❤❤ 几天不见 &#xff0c;甚是想念&#xff01;哈咯大家好又是我大伟&#xff0c;五一的假期已经结束&#xff0…

rt-thread 挂载romfs与ramfs

参考&#xff1a; https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/tutorial/qemu-network/filesystems/filesystems?id%e4%bd%bf%e7%94%a8-romfs https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/tutor…

实操Linux磁盘管理(分区、格式化、挂载)

在Linux系统中&#xff0c;磁盘管理是一个必学的知识点。正确地进行磁盘分区、格式化和挂载可以确保我们能够充分利用磁盘空间并高效地存储和访问数据。 相比于Windows系统中的简单盘符管理&#xff0c;Linux中的磁盘管理更加复杂且灵活。在Linux系统中&#xff0c;一切设备都…

一款简易的免费抽奖软件

一、介绍 这款抽奖软件设计简洁&#xff0c;操作便捷。用户可以轻松将参与名单通过EXCEL文件导入至程序中&#xff0c;并可根据需要设定各类奖品和对应的中奖人数。在选定了奖品后&#xff0c;用户只需点击“开始”按钮&#xff0c;随后再按下“暂停”按钮&#xff0c;软件便会…

C++/Qt 小知识记录6

工作中遇到的一些小问题&#xff0c;总结的小知识记录&#xff1a;C/Qt 小知识6 dumpbin工具查看库导出符号OSGEarth使用编出的protobuf库&#xff0c;报错问题解决VS2022使用cpl模板后&#xff0c;提示会乱码的修改设置QProcess调用cmd.exe执行脚本QPainterPath对线段描边处理…

pycharm本地文件更新至虚拟机

tools–>deployment–>configuration root path的路径要跟远程路径对齐&#xff0c;方便后续运行 mapping映射&#xff0c;本地路径和远程路径 点击Browse 可以在右侧同步查看更新情况

【Element-UI快速入门】

文章目录 **Element-UI快速入门****一、Element-UI简介****二、安装Element-UI****三、引入Element-UI****四、使用Element-UI组件****五、自定义Element-UI组件样式****六、Element-UI布局组件****七、Element-UI表单组件****八、插槽&#xff08;Slots&#xff09;和主题定制…

VS项目Debug下生成的EXE在生产机器上运行

使用Visual Studio开发应用程序时&#xff0c;为了临时在非开发机上看一下效果&#xff0c;就直接把Debug下的文件全部拷贝到该机器上&#xff0c;直接双击exe运行。双击之后&#xff0c;没有直接打开应用程序&#xff0c;而是弹出了一个Error弹框。  赶快在网上搜了一遍&…

浏览器执行渲染原理

一、事件循环 事件循环&#xff08;Event Loop&#xff09;是JavaScript的执行环境的核心概念之一&#xff0c;它负责处理JavaScript中的异步操作和执行顺序。事件循环使得JavaScript能够在单线程上有效地处理并发&#xff0c;同时保持编程模型的简单性。 以下是事件循环的一…

智慧油田三维电子沙盘系统

深圳易图讯科技(www.3dgis.top)智慧油田三维电子沙盘系统采用三维GIS、大数据、云计算、虚拟现实、物联网、AI等前沿技术&#xff0c;支持无人机航拍、高清卫星影像、DEM高程数据、矢量数据、无人机倾斜摄像、BIM模型、点云、城市白模、等高线、标高点等数据融合和切换&#xf…

SCI论文发表:寻找论文选题的7种方法 (建议收藏)!

我是娜姐 迪娜学姐 &#xff0c;一个SCI医学期刊编辑&#xff0c;探索用AI工具提效论文写作和发表。 近期有好几个同学问娜姐关于论文选题的问题&#xff1a;什么样的选题是好的选题-有价值好发表。这篇来分享7种寻找有效选题的方法。 在我们寻找论文选题的过程中&#xff0c;…

猫头虎分享已解决Bug || **Babel转换器下载问题** Failed to resolve babel-loader dependency`

猫头虎分享已解决Bug &#x1f42f; || Babel转换器下载问题 &#x1f6ab;Failed to resolve babel-loader dependency 博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a…

mapreduce | 自定义Partition分区(案例1)

1.需求 将学生成绩&#xff0c;按照各个成绩降序排序&#xff0c;各个科目成绩单独输出。 # 自定义partition 将下面数据分区处理&#xff1a; 人名 科目 成绩 张三 语文 10 李四 数学 30 王五 语文 20 赵6 英语 40 张三 数据 50 李四 语文 10 张三 英语 70 李四 英语…

BGP大型实验

一.实验拓扑 二.实验要求 1.As1中存在两个环回个地址为192.168.1.0/24&#xff0c;该地址不能在任何协议中宣告&#xff1b; AS3中存在两个环回&#xff0c;一个地址为192.168.2.0/24&#xff0c;不能在任何协议中宣告&#xff0c;最终要求这两个环回可以ping通&#xff1b; …