当前位置: 首页 > news >正文

C语言线程

线程

多个进程中通过轮流使用CPU来完成自己的任务,如果多个进程的操作都一模一样那么CPU的开销就会很大,因为进程的地址都是私有的,如果CPU对相同的操作只执行一次,后面再遇到直接去获取即可,这样大大降低了CPU的开销,如此就引出了线程。

 所谓线程就是一个轻量级的进程。
在同一进程中可以创建多个线程共享这个进程的地址空间。
线程使操作系统可调度的最小单位。

对于操作系统而言,线程与进程没有区别。

线程的基本操作

  1. 创建线程

     2.删除线程

     3.控制线程

线程相关函数

  1. pthread_join(pthread_t tid, void ** retval) : 等待子线程结束后回收资源。

    1. 参数1:线程号。
    2. 参数2:线程函数的返回结果。

pthread_exit(void*); 线程函数中的返回函数,跟return类似。 

  1. pthread_detach(pthread_t id); // 主线程中调用线程分离,子线程中调用将子线程设置为游离态,主线程不再阻塞式等待子线程完成后才进行自己的工作,该函数会将子线程的回收工作交给内核去做。

  2. pthread_self(); // 获取当前线程的ID

线程的状态

  1. 新建:新创建的一个线程。
  2. 就绪:准备运行的线程。
  3. 运行:正在运行的线程。
  4. 等待:也叫阻塞。
  5. 死亡:运行结束的线程。

多线程

同步与互斥

信号量

 

 互斥锁

pthread_mutex_destroy(pthread_mutex_t *mutex); 销毁锁。
pthread_mutex_lock(pthread_mutext_t *mutex); 上锁。
pthread_mutex_unlock(pthread_mutext_t *mutex); 解锁。
————————————————

传统的进程间的通信
无名管道

管道的创建是放在内存的内核区中,所以是不能很直观的看到管道的。

linux中管道也是文件。(管道文件)所以管道成功创建后会返回两个文件描述符,分别是读端和写端。(fd[0]读和fd[1]写)

一般来说两个进程一个发一个收,那么一端关闭fd[0]读操作,另一端关闭fd[1]写操作。

因为管道在内核区,用户对其操作只能用系统调用write和read操作,而不能用fwrite和fread

管道只能实现具有血缘关系的进程才能进行通信,否则会出现我创建的管道你找不到的情况。

管道的创建与关闭

 

有名管道(命名管道)

无名管道必须是有血缘关系的进程之间通信,但是实际情况并不是这样,现实中大多需要没有任何关系的进程间通信。这时就需要使用有名管道进行通信。

那么怎么能使不同进程间都找到这个管道呢?这时就有了管道文件,不同进程间可以通过对这个文件的读写来实现通信。

实际上这个管道文件存在于文件系统,这个文件的作用只是为了让没有血缘关系的两个进程能够找到存储在内核区中的同一个管道。读写的操作实际上还是通过内核区的管道。

把这个管道文件看作是内核区中的管道的名字以此来找到同一个内核区的管道,所以称其为有名管道。

  1. 特点:

    1. 可以实现任意两个进程之间的通信。
    2. 通信时双方通过一个管道文件进行操作,但实际上通过管道文件标识内核区管道来进行读写操作。这个文件的大小始终为0
    3. 管道文件是存在于文件系统中的。

 

 

 

 

  1. 信号量

    无名信号量:解决线程之间的同步与互斥。无名信号量的使用案例:

 

 

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

// 临界区:盘子
int plate = 4; // 最多只能放4个水果
int orange = 0; // 盘子中橘子的数量
int apple = 0; // 盘中苹果的数量

// 消费者最大值
int son_max = 5; // 儿子最多吃5个橘子
int girl_max = 5;// 女儿最多吃5个苹果

// 创建互斥锁,同一时刻只能有一个人放水果或拿水果
pthread_mutex_t production;
pthread_mutex_t consumer;

// 信号量
sem_t s_dad, s_mom, s_son, s_girl;

void* thread_dad(void *sp)
{
    while(1)
    {
        pthread_mutex_lock(&production);// 放水果占有盘子
        sem_wait(&s_dad);
        if(0 == son_max){
            printf("儿子吃饱了\n");
            sem_destroy(&s_son);
            pthread_mutex_unlock(&production);
            pthread_exit(NULL);
        }

        if(plate > 0){
            printf("爸爸放了一个橘子\n");
            --plate; // 盘子容量-1
            ++orange;// 橘子个数+1
        }
        else{
            printf("盘满了\n");
        }
        sem_post(&s_son); // 告诉儿子盘中有水果了
        pthread_mutex_unlock(&production);// 放完水果释放盘子
        sleep(1);
    }
    pthread_exit(NULL);
}

void* thread_mom(void *sp)
{
    while(1)
    {
        pthread_mutex_lock(&production);// 占用盘子放水果
        sem_wait(&s_mom);
        if(0 == girl_max){
            printf("女儿吃饱了\n");
            sem_destroy(&s_mom);
            pthread_mutex_unlock(&production);
            pthread_exit(NULL);
        }

        if(plate > 0){
            printf("妈妈放了一个苹果\n");
            --plate;// 盘子容量-1
            ++apple;// 苹果个数+1
        }
        else{
            printf("盘满了\n");
        }
        sem_post(&s_girl); // 告诉女儿盘中有水果了
        pthread_mutex_unlock(&production);// 释放盘子
        sleep(1);
    }
    pthread_exit(NULL);
}

void* thread_son(void *sp)
{
    while(1)
    {
        pthread_mutex_lock(&consumer);// 占用盘子拿橘子
        sem_wait(&s_son);

        if(plate == 4){
            printf("儿子说盘子空了\n");
            sem_post(&s_dad);// 通知爸爸放橘子
            pthread_mutex_unlock(&consumer);
            sleep(1);
            continue;
        }

        if(orange > 0){
            printf("儿子吃掉了一个橘子\n");
            ++plate;// 盘子容量+1
            --orange;// 橘子数量-1
            --son_max;//肚量-1
            if(son_max == 0){
                printf("吃饱了\n");
                pthread_mutex_unlock(&consumer);
                sem_post(&s_dad);// 告诉爸爸不要放橘子了
                sem_destroy(&s_son);// 不吃了
                pthread_exit(NULL);
            }
            sleep(1);
        }
        else{
            printf("没有橘子了\n");
            sleep(1);
        }
        sem_post(&s_dad);// 通知爸爸做橘子
        pthread_mutex_unlock(&consumer);// 释放拿的权限
        sleep(1);

    }
    pthread_exit(NULL);
}

void* thread_girl(void *sp)
{
    while(1)
    {
        pthread_mutex_lock(&consumer);// 占用盘子准备拿水果
        sem_wait(&s_girl);

        if(plate == 4){
            printf("女儿说盘子空了\n");
            sem_post(&s_mom);
            pthread_mutex_unlock(&consumer);
            sleep(1);
            continue;
        }
        if(apple > 0){
            printf("女儿吃掉了一个苹果\n");
            ++plate;// 盘子容量+1
            --apple;// 苹果数量-1
            --girl_max;// 肚量-1
            if(girl_max == 0){
                printf("吃饱了\n");
                pthread_mutex_unlock(&consumer);
                sem_post(&s_mom);// 告诉妈妈不要放苹果了
                sem_destroy(&s_girl);// 不吃了    
                pthread_exit(NULL);
            }
            sleep(1);
        }
        else{
            printf("盘中没有苹果了\n");
            sleep(1);
        }
        sem_post(&s_mom);// 通知妈妈放苹果
        pthread_mutex_unlock(&consumer);
        sleep(1);
    }
    pthread_exit(NULL);
}


int main()
{
    pthread_mutex_init(&production, NULL);
    pthread_mutex_init(&consumer, NULL);
    sem_init(&s_son, 0, 0);
    sem_init(&s_girl, 0, 0);
    sem_init(&s_dad, 0, 1);
    sem_init(&s_mom, 0, 1);

    // 生产者:爸爸往盘中放橘子,妈妈放苹果
    pthread_t dad = 1, mom = 2;
    pthread_create(&dad, NULL, thread_dad, NULL);// 爸爸
    pthread_create(&mom, NULL, thread_mom, NULL);// 妈妈


    // 消费者:儿子吃橘子,女儿吃苹果
    pthread_t son = 3, girl = 4;
    pthread_create(&son, NULL, thread_son, NULL);// 儿子
    pthread_create(&girl, NULL, thread_girl, NULL);// 女儿

    pthread_join(dad, NULL);
    pthread_join(mom, NULL);
    pthread_join(son, NULL);
    pthread_join(girl, NULL);

    pthread_mutex_destroy(&production);
    pthread_mutex_destroy(&consumer);

    return 0;
}

有名信号量:解决进程之间的同步与互斥。
1. 打开双方都认识的有名信号量文件(双方都可以创建)
2. P操作:sem_wait()
3. V操作:sem_post()
4. 关闭有名信号量:sem_close()
5. 删除创建的有名信号量:sem_unlink()
6. 有名信号量创建后在 /dev/shm 下

 

共享内存
其高效是因为,内核区内存的物理地址通过映射 到用户区的虚拟地址,用户通过该地址直接完成读写操作。

是一种最为高效的进程间通信方式,进程可以直接读写共享内存,而不需要任何数据的拷贝。
为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间。
进程就可以直接读写这一内存区而不需要进行数据的拷贝,从而大大提高效率。
由于多个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等。
共享内存中的数据是一直存在的除非删除这个共享内存,不像管道读完后管道中就没有数据了。
共享内存的使用
1. 创建/打开共享内存。ftok()函数产生key值,shmget()函数通过key值创建共享内存

映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问。

撤销共享内存映射。(也就是分离)

删除共享内存对象

消息队列

  1. 特点:
    1. 存储在内核中。
    2. 可以按照类型读取消息。
  2. 流程
    1. 产生key值。 ftok()函数

    2. 创建消息队列的通道。

添加消息。 

 

读取消息。

 

删除消息。

 

相关文章:

C语言线程

线程 多个进程中通过轮流使用CPU来完成自己的任务&#xff0c;如果多个进程的操作都一模一样那么CPU的开销就会很大&#xff0c;因为进程的地址都是私有的&#xff0c;如果CPU对相同的操作只执行一次&#xff0c;后面再遇到直接去获取即可&#xff0c;这样大大降低了CPU的开销…...

自闭症寄宿学校 vs. 日常教育:为孩子提供更多可能

在探索自闭症儿童的教育路径时&#xff0c;家长们往往面临一个重大的选择&#xff1a;是选择传统的日常教育环境&#xff0c;还是寻找专为自闭症儿童设计的寄宿学校&#xff1f;广州的星贝育园自闭症儿童寄宿制学校&#xff0c;以其独特的教育模式和全方位的关怀体系&#xff0…...

RxSwift系列(二)操作符

一、变换操作符&#xff1a;buffer、map、compactMap等 1.buffer buffer方法作用是缓冲组合&#xff0c;第一个参数是缓冲时间&#xff0c;第二个参数是缓冲个数&#xff0c;第三个参数是线程。缓存 Observable 中发出的新元素&#xff0c;当元素达到某个数量&#xff0c;或者…...

Gin框架简易搭建(3)--Grom与数据库

写在前面 项目地址 个人认为GORM 指南这个网站是相比较之下最为清晰的框架介绍 但是它在环境搭建阶段对于初学者而言不是很友好&#xff0c;尤其是使用mysql指令稍有不同&#xff0c;以及更新的方法和依赖问题都是很让人头疼的&#xff0c;而且这些报错并非逻辑上的&#xf…...

JavaScript模块化-CommonJS规范和ESM规范

1 ES6模块化 1.1 ES6基本介绍 ES6 模块是 ECMAScript 2015&#xff08;ES6&#xff09;引入的标准模块系统&#xff0c;广泛应用于浏览器环境下的前端开发。Node.js环境主要使用CommonJS规范。ESM使用import和export来实现模块化开发从而解决了以下问题&#xff1a; 全局作用…...

解决银河麒麟V10中的apt Lock异常

解决银河麒麟V10中的apt Lock异常 一、查找并杀掉apt进程二、删除锁文件三、重新尝试apt命令 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在使用银河麒麟V10的apt命令时&#xff0c;如果遇到lock异常&#xff0c;可以按以下步骤解决&…...

windows11环境安装lua及luarocks(踩坑篇)

一、lua安装及下载 官方地址&#xff1a; Lua Binaries Download 从这里就有坑了&#xff0c;下载后先解压win64_bin.zip&#xff0c;之后解压lib&#xff0c;用lib中的文件替换win64的&#xff0c;并把include文件夹复制过去&#xff0c;之后复制并重命名lua54&#xff0c;方…...

Glide基本用法及With方法源码解析

文章目录 引入优点 使用步骤导入依赖权限使用 其他用法占位符错误图片后备回调符圆角过渡动画大小调整gif缩略图 使用RequestOptions缓存机制设置缓存策略清理缓存 使用集成库OkHttpVolley with源码解析getRetrieverGlide.getinitializeGlide getRequestManagerRetriever Reque…...

html中的文本标签(含标签的实现案例)

目录 1.标题标签 2.标题标签的align属性 3.段落标签 4.水平线标签hr 5.换行标签br 6.文本样式标签font ​编辑7.文本格式化标签 8.文本语义标签 1&#xff09;时间time标签 2&#xff09;文本高亮Mark标签 3&#xff09;cite标签 9.特殊字符标签 10.图像标签img 附录&#xff…...

通信协议感悟

本文结合个人所学&#xff0c;简要讲述SPI&#xff0c;I2C&#xff0c;UART通信的特点&#xff0c;限制。 1.同步通信 UART&#xff0c;SPI&#xff0c;I2C三种串行通讯方式&#xff0c;SPI功能引脚为CS&#xff0c;CLK&#xff0c;MOSI&#xff0c;MISO&#xff1b;I2C功能引…...

IDEA几大常用AI插件

文章目录 前言列表GPT中文版TalkXBito AIIDEA自带的AI 前言 最近AI、GPT特别火&#xff0c;IDEA里面又有一堆插件支持GPT&#xff0c;所以做个专题比较一下各个GPT插件 列表 先看idea的plugins里支持哪些&#xff0c;搜索“GPT”之后得到的&#xff0c;我用下来感觉第一第二和…...

51单片机学习第六课---B站UP主江协科技

DS18B20 1、基本知识讲解 2、DS18B20读取温度值 main.c #include<regx52.h> #include"delay.h" #include"LCD1602.h" #include"key.h" #include"DS18B20.h"float T; void main () {LCD_Init();LCD_ShowString(1,1,"temp…...

sadTalker本地编译

SadTalker一款开源的可生成逼真的人像动画的工具。它利用深度学习技术&#xff0c;根据输入的图像和音频&#xff0c;生成具有生动表情和动作的视频。用户可以通过上传照片或使用预设的模型&#xff0c;轻松创建个性化的动画内容. 以上是官网的图, 下边是本地部署生成的,效果差…...

强化学习核心概念与公式总结

强化学习核心概念与公式总结 1. 核心概念 1.1 智能体(Agent)和环境(Environment) 智能体:学习和做决策的实体环境:智能体交互的外部系统1.2 状态(State) 描述环境在特定时刻的情况1.3 动作(Action) 智能体可以执行的操作1.4 奖励(Reward) 环境对智能体动作的即时反馈1.5 策…...

基础算法--双指针【概念+图解+题解+解释】

更多精彩内容..... &#x1f389;❤️播主の主页✨&#x1f618; Stark、-CSDN博客 本文所在专栏&#xff1a; 数据结构与算法_Stark、的博客-CSDN博客 其它专栏&#xff1a; 学习专栏C语言_Stark、的博客-CSDN博客 项目实战C系列_Stark、的博客-CSDN博客​​​​​​ 座右铭&a…...

国产化系统/鸿蒙开发足浴店收银源码-收缩左侧———未来之窗行业应用跨平台架构

一、左侧展开后 二、代码 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head><title></title><meta http-equiv"Content-Type" content"text/html; charsetUTF-8"><style t…...

如何从硬盘恢复丢失/删除的视频

您是否想知道是否可以恢复已删除的视频&#xff1f; 幸运的是&#xff0c;您可以使用奇客数据恢复从硬盘驱动器、SD 卡和 USB 闪存驱动器恢复已删除的视频文件。 你有没有遇到过这样的情况&#xff1a;当你随机删除文件以释放空间时&#xff0c;你不小心按下了一些重要视频的…...

《Effective C++》第三版——设计与声明(1)

参考资料&#xff1a; 《Effective C》第三版 注意&#xff1a;《Effective C》不涉及任何 C11 的内容&#xff0c;因此其中的部分准则可能在 C11 出现后有更好的实现方式。 条款 18&#xff1a;让接口容易被正确使用&#xff0c;不易被误用 好的接口很容易被正确使用&…...

数值计算的程序设计问题举例

### 数值计算的程序设计问题 #### 1. 结构静力分析计算 **涉及领域**&#xff1a;工程力学、建筑工程 **主要问题**&#xff1a;线性代数方程组&#xff08;Linear Algebraic Equations&#xff09; **解释说明**&#xff1a; 在结构静力分析中&#xff0c;我们需要解决复杂的…...

Java之方法的使用

修饰符 返回值 方法名称&#xff08;形式参数&#xff09;{ } 当无参数的时候形式参数中什么都不写。 列如求两个数相加 修饰符可有可无。 方法重载&#xff1a; 1.方法名相同 2.参数列表不同 3。返回值不影响重载...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入&#xff0c;一个是通过INMP441麦克风模块采集音频&#xff0c;一个是通过PCM5102A模块播放音频&#xff0c;那如果我们将两者结合起来&#xff0c;将麦克风采集到的音频通过PCM5102A播放&#xff0c;是不是就可以做一个扩音器了呢…...

【JavaSE】绘图与事件入门学习笔记

-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角&#xff0c;以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向&#xff0c;距离坐标原点x个像素;第二个是y坐标&#xff0c;表示当前位置为垂直方向&#xff0c;距离坐标原点y个像素。 坐标体系-像素 …...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式

今天是关于AI如何在教学中增强学生的学习体验&#xff0c;我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育&#xff0c;这并非炒作&#xff0c;而是已经发生的巨大变革。教育机构和教育者不能忽视它&#xff0c;试图简单地禁止学生使…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)

macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 &#x1f37a; 最新版brew安装慢到怀疑人生&#xff1f;别怕&#xff0c;教你轻松起飞&#xff01; 最近Homebrew更新至最新版&#xff0c;每次执行 brew 命令时都会自动从官方地址 https://formulae.…...

Spring Security 认证流程——补充

一、认证流程概述 Spring Security 的认证流程基于 过滤器链&#xff08;Filter Chain&#xff09;&#xff0c;核心组件包括 UsernamePasswordAuthenticationFilter、AuthenticationManager、UserDetailsService 等。整个流程可分为以下步骤&#xff1a; 用户提交登录请求拦…...

恶补电源:1.电桥

一、元器件的选择 搜索并选择电桥&#xff0c;再multisim中选择FWB&#xff0c;就有各种型号的电桥: 电桥是用来干嘛的呢&#xff1f; 它是一个由四个二极管搭成的“桥梁”形状的电路&#xff0c;用来把交流电&#xff08;AC&#xff09;变成直流电&#xff08;DC&#xff09;。…...

【Linux】Linux安装并配置RabbitMQ

目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的&#xff0c;需要先安…...