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. 创建消息队列的通道。

添加消息。 

 

读取消息。

 

删除消息。

 

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

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

相关文章

【AI战略思考5】工欲善其事,必先利其器。我的利器是什么?

目录 导言1.不要忽视时间本身复利的巨大威力2.只赚自己认知以内的钱&#xff0c;只把握自己能力以内的机会3.多做有难度的事来激发自己的潜力和提升自己4.学会抵制诱惑5.减少冗余思考和冗余操作 导言 工欲善其事&#xff0c;必先利其器。我的利器是什么&#xff1f; 虽然我中考…

【C++拓展(四)】秋招建议与心得

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:C从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习C   &#x1f51d;&#x1f51d; C拓展 1. 前言2. 今年秋招形势到底如何?3. 学历…

Proteus如何添加数码管

1、打开安装好的Proteus&#xff0c;点击上方菜单栏中的“库”&#xff0c;再选择“从库选取零件”&#xff0c;或者在左侧元件列表中单击鼠标右键&#xff0c;再点击右键菜单中的“从库中挑选”选项。 2、之后在元器件库中&#xff0c;点击类别中的“Optoelectronics”&#…

景联文科技精准数据标注:优化智能标注平台,打造智能未来

景联文科技是一家致力于为人工智能提供全面数据标注解决方案的专业公司。 拥有一支由经验丰富的数据标注师和垂直领域专家组成的团队&#xff0c;确保数据标注的质量和专业性。 自建平台功能一站式服务平台&#xff0c;提供从数据上传、标注、审核到导出的一站式服务&#xff0…

熔断降级 请求合并 请求缓存 线程池隔离 信号量隔离 openfeign整合Hystrix

〇、Hystrix的解决策略和预防措施&#xff1a; 熔断降级是解决远程调用已经出现问题的解决方案。 请求合并 请求缓存 线程池隔离 信号量隔离都是预防性措施 使用Hystrix启动类上需要添加注解开启注解支持&#xff1a; EnableHystrixEnableCircuitBreaker 测试Hystrix需要的依赖…

DAY81服务攻防-开发框架安全SpringBootStruts2LaravelThinkPHPCVE 复现

知识点&#xff1a; 1、PHP-框架安全-Thinkphp&Laravel 2、J2EE-框架安全-SpringBoot&Struts2 常见语言开发框架&#xff1a; PHP&#xff1a;Thinkphp Laravel YII CodeIgniter CakePHP Zend等 JAVA&#xff1a;Spring MyBatis Hibernate Struts2 Springboot等 P…

安宝特案例 | 某知名日系汽车制造厂,借助AR实现智慧化转型

案例介绍 在全球制造业加速数字化的背景下&#xff0c;工厂的生产管理与设备维护效率愈发重要。 某知名日系汽车制造厂当前面临着设备的实时监控、故障维护&#xff0c;以及跨地域的管理协作等挑战&#xff0c;由于场地分散和突发状况的不可预知性&#xff0c;传统方式已无法…

服务器数据恢复—存储映射到服务器上的卷无法挂载的数据恢复案例

服务器存储数据恢复环境&故障&#xff1a; 一台存储上有一组由16块FC硬盘组建了一组raid。存储前面板上的对应10号和13号硬盘的故障灯亮起&#xff0c;存储映射到redhat linux操作系统服务器上的卷挂载不上&#xff0c;业务中断。 服务器存储数据恢复过程&#xff1a; 1、…

18.Linux-配置DNF仓库

DNF仓库产生背景 在现实的场景中&#xff0c;我们经常要安装一些软件包&#xff0c;但由于现场不提供网络。 需要使用光盘或文件下载的方式去安装。 对于linux有两种离线安装方式&#xff1a;二进制文件安装和源码安装 其中二进制文件是比较简单的安装方式&#xff0c;不同的l…

C++学习9.27

1、顺序表、栈、队列都更改成模板类 &#xff08;1&#xff09;顺序表 #include <iostream> #include <cstring>using namespace std;template <typename T1,typename T2,typename T3> class My_string { private:T1 *ptr; //指向字符数组的指针T2…

iwebsec靶场 反序列化关卡通关笔记2-反序列化漏洞示例02

目录 第02关 反序列化漏洞示例02 1.打开靶场 2.源码分析 3.login函数利用 4.show函数利用 5.参数反序列化设计 6.show函数查询orange 7.增加注释语句 8.show函数SQL注入获取密码 &#xff08;1&#xff09;构造SQL语句 &#xff08;2&#xff09;构造序列化 &#…

OJ在线评测系统 后端判题机架构搭建 使用原生实现Java安全管理器环境隔离

原生实现安全管理器环境隔离 限制用户的操作权限 文件 网络 执行 Java安全管理器 SecurityManager 来实现更严格的限制 是 Java 提供的保护 JVM Java安全的机制 可以实现更严格的资源和操作限制 编写安全管理器 只需要继承 SecurityManager类 我们可以从这个参数perm参数拿…

Study--Oracle-09--部署Openfiler存储服务器

一、安装Oracle RAC需要存储&#xff0c;为此搭建安装openfiler用于模拟存储。 openfiler相关镜像包可从官网下载&#xff1a;Downloads | Openfiler 当前最新版本如下&#xff08;该笔记也是基于如下版本&#xff09; 二、安装步骤 https://zhuanlan.zhihu.com/p/519819303…

趋势外推法

趋势外推法主要利用图形识别法和差分法计算&#xff0c;进行模型的基本选择。 一、图形识别法。 这种方法是通过绘制散点图来进行的&#xff0c;即将时间序列的数据绘制成以时间 t 为横轴、时序观察值为纵轴的图形&#xff0c;观察并将其变化曲线与各类函数曲线模型的图形进行…

媒界:吉利星瑞百炼成钢,持续引领中国汽车价值向上

秋风送爽绘秋色&#xff0c;出行良辰恰逢时。9月28日至9月29日&#xff0c;2024安行中国汽车安全科技公益巡展迎来尾声&#xff0c;安行中国携手吉利汽车&#xff0c;步履轻盈地踏入苏州星湖天街&#xff0c;共同呈献一场融合环保科技前沿、安全驾驶理念与深厚文化底蕴的48小时…

设备管理系统-TPM(PC+APP/PDA全流程)高保真Axure原型 源文件分享

随着科技的不断发展&#xff0c;企业对于设备管理的需求也日益增强。为了满足企业在设备管理方面的各种需求&#xff0c;站长为大家整理了一套设备管理系统TPM&#xff08;PCAPP/PDA全流程&#xff09;高保真Axure原型&#xff0c;通过这套原型&#xff0c;企业能够实现对设备的…

Xinstall助力广告主实现精准投放,提升App广告效果!

随着移动互联网的快速发展&#xff0c;App广告投放已成为品牌推广的重要手段。然而&#xff0c;广告投放的效果如何&#xff0c;是否达到了预期的目标&#xff0c;这些问题一直困扰着广告主。今天&#xff0c;我们就来聊聊App广告投放数据统计的痛点&#xff0c;以及Xinstall如…

HBase DML操作代码汇总(表格数据的CRUD操作)

HBase DML操作 DML操作主要是关于对表格内部数据的增删改查。 HbaseDML package org.hbase;import org.apache.hadoop.hbase.Cell; import org.apache.hadoop.hbase.CellUtil; import org.apache.hadoop.hbase.CompareOperator; import org.apache.hadoop.hbase.TableName; i…

性能测试:性能测试计划

性能测试计划是在进行软件或系统的性能测试之前制定的详细计划和指导文件。它描述了所需性能测试的目标、范围、测试环境、资源需求、测试策略、测试用例、时间表等重要信息。 为什么要制定性能测试计划 制定性能测试计划的主要目的是确保性能测试的有效性和可靠性。以下是制…

通过OpenScada在ARMxy边缘计算网关上实现数字化转型

随着工业4.0概念的普及&#xff0c;数字化转型已成为制造业升级的关键路径之一。在此背景下&#xff0c;边缘计算技术因其能够有效处理大量数据、减少延迟并提高系统响应速度而受到广泛关注。ARMxy边缘计算网关&#xff0c;特别是BL340系列&#xff0c;凭借其强大的性能和灵活的…