多线程的基本使用与多线程中条件变量的使用——消费者生产者问题实例

news2024/11/25 11:25:48

多线程的基本使用与多线程中条件变量的使用——消费者生产者问题实例

本文主要涉及多线程的使用方法,通过两个实例来对多线程的使用进行理解,
案例包括:
1.一个线程负责计数,另一个线程负责打印计数值
2.消费者生产者问题

文章目录

  • 多线程的基本使用与多线程中条件变量的使用——消费者生产者问题实例
    • 一、 多线程地基本用法
      • 1.1 多线程概念
      • 1.2 多线程的优势
      • 1.3 常用的多线程函数
      • 1.4 实际案例(一个负责计数一个负责打印)
      • 1.5 usleep在上面案例中地重要性
    • 二、 多线程的条件变量
      • 2.1 基本的条件变量操作:
      • 2.2 实际案例 - 生产者-消费者问题

一、 多线程地基本用法

1.1 多线程概念

线程线程是进程的一部分,是 CPU 调度和分派的基本单位。一个进程可以包含多个线程,这些线程共享进程的资源(如内存),但每个线程都有自己的堆栈和局部变量。

多线程:多线程是指一个进程中有多个执行路径,每个执行路径都称为一个线程。多线程允许程序同时执行多个任务,增强了程序的并发性和响应性。

1.2 多线程的优势

  1. 响应性:当一个线程等待某些操作完成时,其他线程可以继续执行,提高了整体系统的响应性。
  2. 资源共享:多个线程可以共享同一个进程的资源,如内存,文件句柄等。
  3. 经济性:线程的创建和销毁比进程更为经济,因为线程间的上下文切换比进程间的上下文切换要快。

1.3 常用的多线程函数

  1. pthread_create:创建一个新的线程。
  2. pthread_join:等待一个线程结束。
  3. pthread_exit:线程自己退出。
  4. pthread_mutex_initpthread_mutex_lockpthread_mutex_unlock:用于创建和管理互斥锁,确保在某一时刻只有一个线程访问共享资源。
  5. pthread_cond_initpthread_cond_waitpthread_cond_signal:条件变量用于线程间的通信。

1.4 实际案例(一个负责计数一个负责打印)

考虑一个简单的场景:有两个线程,一个线程负责计数,另一个线程负责打印计数值。

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>  // 用于 usleep 函数

int count = 0;
pthread_mutex_t count_mutex;

void *increment_counter(void *arg) {
    for (int i = 0; i < 10; i++) {
        pthread_mutex_lock(&count_mutex);
        count++;
        pthread_mutex_unlock(&count_mutex);
        usleep(300000);  // 休眠 300ms
    }
    pthread_exit(NULL);
}

void *print_counter(void *arg) {
    for (int i = 0; i < 10; i++) {
        pthread_mutex_lock(&count_mutex);
        printf("Count: %d\n", count);
        pthread_mutex_unlock(&count_mutex);
        usleep(300000);  // 休眠 300ms
    }
    pthread_exit(NULL);
}

int main() {
    pthread_t thread1, thread2;

    pthread_mutex_init(&count_mutex, NULL);

    pthread_create(&thread1, NULL, increment_counter, NULL);
    pthread_create(&thread2, NULL, print_counter, NULL);

    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    pthread_mutex_destroy(&count_mutex);

    return 0;
}

在这里插入图片描述

在上述例子中,两个线程并发地增加和打印计数值,为了确保计数的正确性,使用了互斥锁。其中有两个线程:increment_counterprint_counter。其中,一个线程用于增加一个全局计数器 count 的值,而另一个线程则用于打印这个计数器的值。

以下是代码的工作流程:

  1. 全局变量和互斥锁:定义了一个全局变量 count 和一个互斥锁 count_mutex。互斥锁用于确保同时只有一个线程可以修改 count 的值,从而避免了竞争条件。

  2. 增加计数器的线程increment_counter 函数负责增加 count 的值。在每次增加之前,线程会锁定互斥锁,然后在增加后解锁它。此外,为了模拟一些处理时间,线程使用 usleep 函数休眠 300 毫秒。

  3. 打印计数器的线程print_counter 函数负责打印 count 的当前值。它也使用相同的互斥锁来确保在打印之前和之后正确地锁定和解锁。

  4. 主函数:在 main 函数中,首先初始化互斥锁。然后创建两个线程,一个用于增加计数器,另一个用于打印计数器的值。使用 pthread_join 确保主线程等待这两个线程完成后再继续执行。最后,清理互斥锁并退出程序。

运行此程序时,您应该会看到交替的输出,其中每个值都是先增加线程更改后的最新值。由于使用了互斥锁,所以不会出现不一致或竞争条件的情况。

1.5 usleep在上面案例中地重要性

如果没有这个延迟,可能会看到一些奇怪的行为,甚至可能出现全是10的情况。这是因为线程调度的机制。

在多线程环境中,操作系统会为每个线程分配时间片(也称为CPU时间),一个线程在分配的时间片用完后,操作系统可能会将其挂起并切换到另一个线程。这种切换是不确定的,即无法预测两个线程之间的交替顺序。

如果没有延迟,increment_counter 线程可能会迅速地递增 count 到10,然后print_counter线程开始运行,因为没有其他操作或延迟来改变这种行为。因此,可能会看到很多行都是 Count: 10

但是,当在关键部分(如对 count 的递增和打印)添加了延迟时,会更有机会看到这两个线程之间的交互,因为操作系统的线程调度会更频繁地进行线程切换,从而使得递增和打印的操作交错进行。

延迟在这里有助于模拟和增强多线程的交互效果,使得并发问题更容易观察和理解。

二、 多线程的条件变量

条件变量 (Condition Variables) 是一种线程同步机制,它允许一个或多个线程等待某个特定的条件得到满足后再继续执行。它通常与互斥量 (Mutex) 一起使用,以确保在检查条件和等待条件之间的操作是原子的。

2.1 基本的条件变量操作:

  1. 初始化条件变量:

    pthread_cond_t cond_var = PTHREAD_COND_INITIALIZER;
    
  2. 等待条件满足:

    pthread_cond_wait(&cond_var, &mutex);
    

    此函数会释放互斥量并阻塞当前线程,直到另一个线程调用 pthread_cond_signalpthread_cond_broadcast 并重新获得互斥量。

  3. 发送信号:

    pthread_cond_signal(&cond_var);
    

    此函数唤醒一个等待在条件变量上的线程。如果有多个线程等待,那么哪一个会被唤醒是不确定的。

  4. 广播信号:

    pthread_cond_broadcast(&cond_var);
    

    此函数唤醒所有等待在条件变量上的线程。

2.2 实际案例 - 生产者-消费者问题

考虑一个经典的生产者-消费者问题,其中生产者线程在一个有限的缓冲区中放入数据,而消费者线程从缓冲区中取出数据。

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

#define BUFFER_SIZE 10
int buffer[BUFFER_SIZE];
int count = 0;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond_full = PTHREAD_COND_INITIALIZER;
pthread_cond_t cond_empty = PTHREAD_COND_INITIALIZER;

void* producer(void* arg) {
    for (int i = 0; i < 100; i++) {
        pthread_mutex_lock(&mutex);
        while (count == BUFFER_SIZE) {
            pthread_cond_wait(&cond_empty, &mutex);
        }
        buffer[count++] = i;
        printf("Produced: %d\n", i);
        pthread_cond_signal(&cond_full);
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

void* consumer(void* arg) {
    for (int i = 0; i < 100; i++) {
        pthread_mutex_lock(&mutex);
        while (count == 0) {
            pthread_cond_wait(&cond_full, &mutex);
        }
        int value = buffer[--count];
        printf("Consumed: %d\n", value);
        pthread_cond_signal(&cond_empty);
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

int main() {
    pthread_t prod_thread, cons_thread;
    
    pthread_create(&prod_thread, NULL, producer, NULL);
    pthread_create(&cons_thread, NULL, consumer, NULL);

    pthread_join(prod_thread, NULL);
    pthread_join(cons_thread, NULL);

    return 0;
}

在这个例子中,我们有一个缓冲区和一个互斥量来保护它。生产者和消费者线程都会检查缓冲区的状态,并在缓冲区满或空时等待条件变量的信号。当生产者放入数据时,它会唤醒消费者(如果它在等待)。反之亦然,当消费者消费数据时,它会唤醒生产者。这确保了生产者和消费者之间的同步和协调。

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

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

相关文章

【MySQL索引特性】

目录&#xff1a; 前言引入认识磁盘MySQL与存储 索引的理解理解单个Page理解多个Page引入B树结构聚簇索引 VS 非聚簇索引 索引操作创建主键索引唯一索引的创建普通索引的创建查看索引删除索引 总结 前言 剑指offer&#xff1a;一年又10天 引入 索引&#xff0c;是用来提高查询…

【python】python课设 天气预测数据分析及可视化(完整源码)

目录 1. 前言2. 项目结构3. 详细介绍3.1 main.py3.2 GetModel.py3.3 GetData.py3.4 ProcessData.py3.5天气网.html 4. 成果展示 1. 前言 本文介绍了天气预测数据分析及可视化的实现过程使用joblib导入模型和自定义模块GetModel获取模型&#xff0c;输出模型的MAE。使用pyechart…

ansible的控制语句

本章内容主要介绍 playbook 中的控制语句 使用when判断语句block-rescue判断循环语句 一个play中可以包含多个task&#xff0c;如果不想所有的task全部执行&#xff0c;可以设置只有满足某个条件才执行这个task&#xff0c;不满足条件则不执行此task。本章主要讲解when 和 blo…

Linux安装及管理程序

一、Linux应用程序管理 1、应用程序与系统命令的关系 1.对比系统命令和应用程序的不同 位置&#xff1a; Linux中一切皆为文件 演示内部命令和外部命令 位置 应用程序位置 用途&#xff1a; 命令主要处理系统的基本操作&#xff08;复制&#xff0c;配置&#xff09; 应用程…

大模型工具_Langchain-Chatchat

https://github.com/chatchat-space/Langchain-Chatchat 原Langchain-ChatGLM 1 功能 整体功能&#xff0c;想解决什么问题 基于 Langchain 与 ChatGLM 等LLM模型&#xff0c;搭建一套针对中文场景与开源模型&#xff0c;界面友好、可离线运行的知识库问答解决方案。 当前解决…

米勒电容与米勒效应

米勒电容与米勒效应 米勒效应米勒效应的形成原理及分析米勒效应的危害和改进 米勒效应 Ciss CGE CGC 输入电容 Coss CGC CEC 输出电容 Crss CGC 米勒电容 下面我们以MOS中的米勒效应来展开说明&#xff1a; 米勒效应在MOS驱动中臭名昭著&#xff0c;它是由MOS管的米勒电容引发…

运行时和编译时使用的so库不同是否影响可执行文件执行

引子 近日遇到如下问题: 1.如果可执行文件依赖的so库在编译和执行阶段使用的名字一样&#xff0c;但是内容不一样&#xff0c;比如运行时相比于编译时在so库里增加了几个api定义&#xff0c;so库还可以正常使用吗&#xff1f; 2.如果可执行文件依赖的so库在编译和执行阶段使用的…

buuctf-Misc 题目解答分解94-96

94.[SUCTF 2019]Game 在源码包里面 有一个静态页面和一些样式表 在index,html 中看到了flag base32 解码 得到flag suctf{hAHaha_Fak3_F1ag} 但是显示不对 还有一张图片 进行数据提取发现base64 U2FsdGVkX1zHjSBeYPtWQVSwXzcVFZLu6Qm0To/KeuHg8vKAxFrVQ 解密后发现是Sal…

编译原理--词法分析C++

一、实验项目要求 1.实验目的 通过设计编制调试一个具体的词法分析程序&#xff0c;加深对词法分析原理的理解。并掌握在对程序设计语言源程序进行扫描过程中将其分解为各类单词的词法分析方法。 编制一个读单词过程&#xff0c;从输入的源程序中&#xff0c;识别出各个具有…

XUbuntu22.04之跨平台容器格式工具:MKVToolNix(二百零三)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

29.Java程序设计-基于Springboot的幼儿园管理系统的设计与实现

1. 引言 背景介绍&#xff1a;幼儿园管理系统的必要性和重要性。研究目的&#xff1a;设计一个基于Spring Boot的系统以优化幼儿园管理流程。论文结构概览。 2. 需求分析 用户需求&#xff1a;不同用户&#xff08;管理员、老师、家长&#xff09;的需求分析。功能需求&…

多次触发FastJson漏洞的AutoType机制,你了解吗?

一个反序列化问题 在一次日志巡检过程中&#xff0c;发现线上业务出现报错。线上业务场景是&#xff1a;调用三方restful接口&#xff0c;根据接口返回json字符串内容&#xff0c;进行反序列化处理&#xff0c;业务中使用的json处理工具是FastJson(v1.2.71)。 报错是使用fast…

【Linux系统编程二十三】:(信号2)--信号的保存

【Linux系统编程二十三】&#xff1a;信号的保存 一.信号的保存1.阻塞信号2.sigset_t类型(位图)3.block表4.handler表5.pending表 二.实验验证三.信号的其他概念 一.信号的保存 信号发送本质上是操作系统发送信号&#xff0c;而进程PCB内部有一个位图用来表示是否接收到信号。…

T-Dongle-S3开发笔记——创建工程

创建Hello world工程 打开命令面板 方法1&#xff1a;查看->命令面板 方法2&#xff1a;按F1 选择ESP-IDF:展示示例项目 创建helloworld 选择串口 选择芯片 至此可以编译下载运行了 运行后打印的信息显示flash只有2M。但是板子上电flash是W25Q32 4MB的吗 16M-bit

高级RGA(二):父文档检索器

在我之前写的<<使用langchain与你自己的数据对话>>系列博客中&#xff0c;我们介绍了利用大型语言模型LLM来检索文档时的过程和步骤&#xff0c;如下图所示&#xff1a; 我们在检索文档之前&#xff0c;通常需要对文档进行切割&#xff0c;然后将其存入向量数据库如…

用友时空KSOA UploadImage任意文件上传漏洞

漏洞描述 用友时空 KSOA 是根据流通企业前沿的IT需求推出的统的IT基础架构&#xff0c;它可以让流通企业各个时期建立的 IT 系统之间彼此轻松对话。由于用友时空设备开放了文件上传功能&#xff0c;但未鉴权且上传的文件类型、大小、格式、路径等方面进行严格的限制和过滤&…

企业知识库在跨地域团队协作中的价值

随着全球化进程的不断加速&#xff0c;越来越多的企业开始面临跨地域协作的挑战。在这种背景下&#xff0c;企业知识库作为一种重要的知识管理工具&#xff0c;对于提高团队协作效率、促进知识共享与创新具有不可替代的价值。接下来就说一下知识库在跨地域团队协作中的重要性及…

JVM简单学习

jvm与字节码 jvm只需关注字节码文件 jvm由哪些部分构成 1.类加载子系统&#xff0c;将磁盘中的字节码文件加载到方法区的内存空间中 类加载器分两种&#xff1a;引导类加载器是jvm底层中用C和C语言写的 各个默认的类加载器的不同区别在于 各自默认负责要加载的类的目录不一…

web前端游戏项目-辨色大比拼【附源码】

web前端游戏项目-辨色大比拼【附源码】 《辨色大比拼》是一个旨在测试和提升玩家颜色识别能力的在线游戏。在游戏中&#xff0c;玩家将通过辨识颜色来解谜并推进游戏进程。辨色大比拼也是一个寓教于乐的游戏&#xff0c;它不仅提供了一个有趣的辨色挑战&#xff0c;还能帮助玩…

通过 Higress Wasm 插件 3 倍性能实现 Spring-cloud-gateway 功能

作者&#xff1a;韦鑫&#xff0c;Higress Committer&#xff0c;来自南京航空航天大学分布式系统实验室 导读&#xff1a;本文将和大家一同回顾 Spring Cloud Gateway 是如何满足 HTTP 请求/响应转换需求场景的&#xff0c;并为大家介绍在这种场景下使用 Higress 云原生网关的…