I\O进程线程(Day30)

一、学习内容

  1. 多线程基础

    1> 线程是任务器调度的基本单位,是进程的一个执行单元

    2> 一个进程中可以包含多个线程,但是至少要包含一个线程称为主线程

    3> 一个进程中的多个线程共享进程的资源,不会为线程再 单独分配内存空间

    4> 线程的切换比进程的切换效率会更高,而且所占用内存会更小(8K左右)

    5> 对于多线程编程,需要使用第三方库,pthread库,使用相关函数后,编译程序时,需要加上库的链接: -lpthread

    6> 多线程也可以实现多任务的并发执行,并且比多进程效率会更高,所占内存更小,所以在实现并发操作中,一般常用多线程实现,而不是多进程

  2. 多线程的编程

    • pthread_create(创建线程)

      int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
      • 返回值
        成功返回0, 失败返回一个错误码(该错误码是pthread库中定义的错误码,而不是内核定义的错误码)
      • 参数

        参数1:创建的线程的线程号,使用地址传递进行返回给主线程

        参数2:线程的属性,一般填NULL,表示使用默认的线程属性

        参数3:是线程体函数指针,需要传递一个函数名,该函数返回值类型为void* , 参数为 void *

        参数4:当前线程向新线程传递的数据,也就是参数3的参数

      • 功能
        在当前进程中,启动一个线程
    • pthread_self(线程号获取)

      pthread_t pthread_self(void);
      • 返回值
        当前线程的线程号
      • 参数
      • 功能
        获取当前线程的线程号
    • pthread_exit(退出线程)

      void pthread_exit(void *retval);
      • 返回值
      • 参数
        线程退出时的状态,一般填NULL
      • 功能
        退出当前线程
    • pthread_join(线程资源的回收)

      int pthread_join(pthread_t thread, void **retval);
      • 返回值
        成功返回0,失败返回一个错误码
      • 参数

        参数1:要回收的线程id号

        参数2:接受线程退出时的状态,一般为NULL

      • 功能
        阻塞等待指定线程的退出,并将其资源吸收到自身身上
    • pthread_detach(线程资源回收)

      int pthread_detach(pthread_t thread);
      • 返回值
        成功返回0,失败返回一个错误码
      • 参数
        要被分离的线程号
      • 功能
        非阻塞形式,将线程设置成分离态,被设置成分离态的线程退出后,其资源由系统进行回收
    • 多线程之间的数据通信

      对于同一个进程的多个线程而言,他们是共用一个进程的资源。虽然不能使用局部变量进行相互通信,但是,可以使用全局变量来进行资源的通信
      • 临界区
        每个线程中,访问临界资源的代码段
      • 临界资源
        多个线程共享全局资源、堆区资源
  3. 竞态

    当多个线程共同使用同一个临界资源,可能会出现当一个线程正在处理数据时,其时间片可能会结束,另一个线程启动后,可将临界资源进行更改,等到再执行该线程时,数据就发生错误了。这种多个线程抢占同一个进程的临界资源的现象
    • 解决方式

      • 互斥机制
        使用互斥锁来完成,互斥锁也是一个特殊的临界资源,当某个线程获得了互斥锁后,其余线程只能等待该线程释放互斥锁后,再进行抢占。同一时刻,只能一个线程拥有互斥锁
      • 同步机制
        使用同步机制,可以完成多个线程有顺序的访问临界资源,这样也起到保护临界资源的作用
  4. 多进程和多线程相关函数的对比

  5. 线程的同步互斥机制

    • 互斥机制
      互斥机制使用的是互斥锁,是一种特殊的临界资源:不能同时被两个不同的线程锁定,如果一个线程试图去锁定一个已经被其他线程锁定的互斥锁时,那个线程将被挂起,直到该线程释放了该互斥锁
      • 销毁锁资源
        int pthread_mutex_destroy(pthread_mutex_t *mutex);
        • 返回值
          成功返回0,失败返回错误码
        • 参数
          互斥锁地址
        • 功能
          销毁互斥锁
      • 释放锁资源
        int pthread_mutex_unlock(pthread_mutex_t *mutex);
        • 返回值
          成功返回0,失败返回错误码
        • 参数
          互斥锁地址
        • 功能
          释放锁资源
      • 获取锁资源
        int pthread_mutex_lock(pthread_mutex_t *mutex);
        • 返回值
          成功返回0,失败返回错误码
        • 参数
          互斥锁地址
        • 功能
          获取锁资源,如果该互斥锁已经被锁定,那么当前线程会在该函数处阻塞
      • 初始化互斥锁
        int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
        • 返回值
          总是返回0
        • 参数

          参数1:互斥锁的地址,用于更改互斥锁内容

          参数2:互斥锁属性,一般填NULL,表示使用默认属性

        • 功能
          初始化一个互斥锁
      • 创建互斥锁
        pthread_mutex_t mutex;
    • 同步机制之无名信号量

      • 概念
        无名信号量中,本质上维护了一个value值,任何一个线程都可以申请该值和释放该值。当某个线程申请该值时,该值就会减少,该操作称为 P 操作。当某个线程释放该资源时,该值就会增加,该操作称为 V 操作。当value的值为0时,申请资源的线程会在申请处阻塞,直到某个线程将该资源增加到大于0
        •  同步机制主要实现的是:生产者消费者模型

        • 1同步:表示的是多个进程有先后顺序的执行

      • 创建一个无名信号量
        sem_t sem;
      • 初始化无名信号量
        int sem_init(sem_t *sem, int pshared, unsigned int value);
        • 返回值
          成功返回0,失败返回-1,并置位错误码
        • 参数

          参数1:要被初始化的无名信号量的地址

          参数2:判断是多进程还是多线程的同步

          0:表示多线程之间的同步
          非0:表示多进程之间的同步(亲缘进程间同步)

          参数3:value的初始值

        • 功能
          初始化一个无名信号量
      • 申请资源P操作
        int sem_wait(sem_t *sem);
        • 返回值
          成功返回0,失败返回-1并置位错误码
        • 参数
          无名信号量的地址
        • 功能
          申请无名信号量的资源,使得无名信号量维护的value值减1操作,如果该无名信号量中的值为0,则当前线程在该函数处阻塞
      • 释放资源 V操作
        int sem_post(sem_t *sem);
        • 返回值
          成功返回0,失败返回-1并置位错误码
        • 参数
          无名信号量地址
        • 功能
          是否无名信号量中的资源,是的无名信号量维护的value值加1操作
      • 销毁无名信号量
        int sem_destroy(sem_t *sem);
        • 返回值
          成功返回0,失败返回-1并置位错误码
        • 参数
          无名信号量的地址,该无名信号量必须要是使用sem_init初始化的无名信号量
        • 功能
          销毁一个无名信号量
  6. 脑图

二、作业

作业1:

使用多线程完成两个文件的拷贝,分支线程1,拷贝前一半,分支线程2拷贝后一半,主线程用于回收分支线程的资源

代码解答:

#include <myhead.h>// 声明全局变量
pthread_mutex_t mutex;  // 互斥锁
int source = -1;        // 源文件描述符
int dest = -1;          // 目标文件描述符
int len = 0;            // 源文件总长度
int half_len = 0;       // 源文件的一半长度// 线程1:负责拷贝源文件的前半部分
void *task1(void *arg)
{char rbuf[128] = "";          // 用于存储读取的数据的缓冲区lseek(source, 0, SEEK_SET);   // 设置源文件指针到文件开头int sum = 0;                  // 记录读取的字节总数while (sum < half_len)        // 循环读取直到读取的字节数达到文件的一半{pthread_mutex_lock(&mutex);  // 加锁,防止其他线程干扰文件操作int res = read(source, rbuf, sizeof(rbuf));  // 读取数据到缓冲区if (res == 0)               // 如果读取到文件末尾,退出循环{pthread_mutex_unlock(&mutex);  // 解锁break;}sum += res;  // 更新已读取的字节总数// 如果超过文件的一半,仅写入多余部分,避免越界写入if (sum > half_len){write(dest, rbuf, res - (sum - half_len));  // 写入目标文件的剩余部分}else{write(dest, rbuf, res);  // 写入读取的完整数据}pthread_mutex_unlock(&mutex);  // 解锁,允许其他线程操作}pthread_exit(NULL);  // 线程结束
}// 线程2:负责拷贝源文件的后半部分
void *task2(void *arg)
{char rbuf[128] = "";           // 用于存储读取的数据的缓冲区lseek(source, half_len, SEEK_SET);  // 设置源文件指针到文件的中间位置int res;while (1){pthread_mutex_lock(&mutex);  // 加锁,防止其他线程干扰文件操作res = read(source, rbuf, sizeof(rbuf));  // 读取数据到缓冲区if (res == 0)               // 如果读取到文件末尾,退出循环{pthread_mutex_unlock(&mutex);  // 解锁break;}write(dest, rbuf, res);  // 将读取的数据写入目标文件pthread_mutex_unlock(&mutex);  // 解锁,允许其他线程操作}pthread_exit(NULL);  // 线程结束
}int main(int argc, const char *argv[])
{pthread_mutex_init(&mutex, NULL);  // 初始化互斥锁// 打开源文件,只读模式if ((source = open("./source.txt", O_RDONLY)) == -1){perror("open source error");  // 打开失败,输出错误信息return -1;}// 打开目标文件,写模式,如果不存在则创建,存在则清空内容if ((dest = open("dest.txt", O_WRONLY | O_CREAT | O_TRUNC, 0664)) == -1){perror("open dest error");  // 打开失败,输出错误信息close(source);  // 关闭已打开的源文件return -1;}// 获取源文件的长度len = lseek(source, 0, SEEK_END);if (len == -1){perror("lseek error");  // 获取文件长度失败,输出错误信息close(source);  // 关闭源文件close(dest);    // 关闭目标文件return -1;}// 计算源文件的一半长度half_len = (len + 1) / 2;// 创建线程1,负责拷贝前半部分pthread_t tid1, tid2;if (pthread_create(&tid1, NULL, task1, NULL) != 0){printf("tid1 create error\n");  // 线程1创建失败return -1;}// 创建线程2,负责拷贝后半部分if (pthread_create(&tid2, NULL, task2, NULL) != 0){printf("tid2 create error");  // 线程2创建失败return -1;}// 打印线程的ID,便于调试printf("tid1=%#lx,  tid2=%#lx\n", tid1, tid2);// 等待线程1、2执行结束pthread_join(tid1, NULL);pthread_join(tid2, NULL);pthread_mutex_destroy(&mutex);  // 销毁互斥锁close(source);  // 关闭源文件close(dest);    // 关闭目标文件printf("拷贝成功\n");  // 拷贝成功提示return 0;
}

成果展现: 

作业2:

互斥锁基础的使用代码

代码解答:

#include <myhead.h>int money=10000; 		//全局变量  临界资源//创建一个互斥锁
pthread_mutex_t mutex;//创建取钱任务1
void *task1(void *arg)
{while(1){ 	sleep(1);//获取锁资源pthread_mutex_lock(&mutex);if(money<500){//释放锁资源pthread_mutex_unlock(&mutex);break;}money-=500;printf("张三消费500元,剩余%d元\n",money);pthread_mutex_unlock(&mutex);}pthread_exit(NULL);
}void *task2(void *arg)
{while(1){sleep(1);//获取锁资源pthread_mutex_lock(&mutex);if(money<200){	pthread_mutex_unlock(&mutex);break;}money-=200;printf("李四消费200元,剩余%d元\n",money);//释放锁资源pthread_mutex_unlock(&mutex);}pthread_exit(NULL);
}/**************主程序****************/
int main(int argc, const char *argv[])
{//初始化互斥锁pthread_mutex_init(&mutex,NULL);//启动多个线程用于消费pthread_t tid1,tid2;if(pthread_create(&tid1,NULL,task1,NULL)!=0){printf("tid1 create error\n");return -1;}if(pthread_create(&tid2,NULL,task2,NULL)!=0){printf("tid2 create error\n");return -1;}printf("tid1=%#lx,tid2=%#lx\n",tid1,tid2);pthread_join(tid1,NULL);pthread_join(tid2,NULL);//销毁锁资源pthread_mutex_destroy(&mutex);return 0;
}

成果展现: 

作业3:

无名信号量基础的使用代码

代码解答:

#include <myhead.h>
//创建一个无名信号量
sem_t sem;//定义生产者线程
void *task1(void *arg)
{int num=5;while(num--){sleep(1);printf("%#lx:我生产了一辆小米SU7\n",pthread_self());//释放无名信号量sem_post(&sem);}//退出线程pthread_exit(NULL);
}void *task2(void *arg)
{int num =5;while(num--){//申请资源sem_wait(&sem);printf("%#lx:我消费了一辆小米SU7\n",pthread_self());}pthread_exit(NULL);
}int main(int argc, const char *argv[])
{//初始化无名信号量sem_init(&sem,0,0);//第一个0:表示用于多线程的同步//第二个0;表示初始化value值为0//创建两个任务线程pthread_t tid1,tid2;if(pthread_create(&tid1,NULL,task1,NULL) !=0){printf("tid1 create error\n");return -1;}if(pthread_create(&tid2,NULL,task2,NULL)!=0){printf("tid2 create error\n");return -1;}//主线程序中printf("tid1=%#lx,tid2=%#lx\n",tid1,tid2);//阻塞等分支线程的结束pthread_join(tid1,NULL);pthread_join(tid2,NULL);sem_destroy(&sem);return 0;
}

成果展现: 

三、总结

学习内容概述:

1. 线程创建与控制

使用 `pthread_create` 创建线程,`pthread_self` 获取线程号,`pthread_exit` 退出线程,`pthread_join` 回收线程资源,`pthread_detach` 实现非阻塞线程分离。

2. 线程间通信

线程共享全局资源,通过全局变量通信,临界区和临界资源的概念。

3. 互斥机制

通过互斥锁(`pthread_mutex_lock` 和 `pthread_mutex_unlock`)保护临界资源,防止竞态条件的发生。

4. 同步机制

使用无名信号量实现线程之间的同步,确保多个线程有序访问临界资源。

 学习难点:

1. 竞态条件和互斥机制

当多个线程同时访问共享资源时,容易引发竞态条件,理解如何通过互斥锁机制保护资源是一大难点。

2. 线程的同步

同步机制的实现(如无名信号量)涉及线程的协调和资源的有序访问,这需要深刻理解信号量的操作模式(P操作和V操作)以及如何避免死锁。

3. 线程回收与分离

线程资源的回收机制涉及到阻塞与非阻塞的区别,如何根据需求选择 `pthread_join` 和 `pthread_detach` 是一个细节上的难点。

主要事项:

1. 互斥锁的使用

在多线程访问临界资源时,务必通过互斥锁保护资源,避免多个线程在同一时间修改共享资源,确保数据的一致性。

2. 信号量同步

合理使用无名信号量来控制线程的同步,确保在某些线程完成资源操作之前,其他线程不会抢先访问资源。

3. 线程的生命周期管理

理解线程的创建、执行、退出和资源回收是多线程编程的重要基础,避免僵尸线程或资源泄漏。

未来学习的重点:

1. 多线程中的高级同步机制

如条件变量和读写锁,这些同步机制在复杂场景中更有效地管理资源访问。

2. 线程池的实现

提升多线程程序的效率和资源管理,减少频繁创建和销毁线程的开销。

3. 多线程的调试技巧

学习如何使用调试工具分析多线程程序中的竞态条件、死锁和线程同步问题。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何启动hive

检查mysql是否启动 通过Navicat测试mysql是否可以连接 找打hive配置文件所在目录 检查连接mysql的账号密码是否正确,如果不正确就要修改为正确的 初始化hive元数据存储的库:schematool -dbType <database_type> -initSchema 检查mysql中是否创建hive数据库,这里看到hive数…

WebGl 使用缓冲区对象绘制多个点

缓冲区对象是WebGL系统中为一块内存区域&#xff0c;可以一次性地向缓冲区对象中填充大量的顶点数据&#xff0c;然后将这些数据保存在其中&#xff0c;供顶点着色器使用。 1.类型化数组类型 在 webgl 中&#xff0c;需要处理大量的相同类型数据&#xff0c;所以引入类型化数组…

机器学习学习笔记-20241018

继续跟着小土堆去学习机器学习 文章目录 Flatten1. Flatten 的作用2. 何时使用 Flatten3. PyTorch 中的 Flatten Sequentia优化器模型的保存与加载模型的完整训练 Flatten 在神经网络中&#xff0c;Flatten 操作是将高维的输入&#xff08;如二维图像或三维特征图&#xff09…

LabVIEW提高开发效率技巧----减少UI更新频率

在LabVIEW开发中&#xff0c;图形化用户界面&#xff08;UI&#xff09;的更新频率对程序的响应速度有着显著影响。频繁的UI更新会占用大量资源&#xff0c;导致系统性能下降。本文将详细介绍如何通过减少UI更新频率来提升LabVIEW程序的运行效率&#xff0c;从多个角度进行分析…

Jenkins入门(二):流水线方式部署多模块Springboot项目

目录 一、环境准备 1. 搭建配置Jenkins (在上一篇基础上进行) 2. 安装mysql 3. 安装redis 4. 配置docker-componse 5. 启动docker-componse 二、脚本准备 1. Jenkinsfile 2. deploy.sh 3. Dockerfile 三、Jenkins流水线配置 新增版本号参数 流水线选择代码里面的Je…

游戏逆向基础-找释放技能CALL

思路&#xff1a;通过send断点然后对send的data参数下写入断点找到游戏里面的技能或者攻击call 进入游戏先选好一个怪物&#xff08;之所以要先选好是因为选怪也会断&#xff0c;如果直接左键打怪的话就会断几次&#xff09; 断下来后对参数下硬件写入断点 硬件断点断下来后先…

Java | Leetcode Java题解之第475题供暖器

题目&#xff1a; 题解&#xff1a; class Solution {public int findRadius(int[] houses, int[] heaters) {Arrays.sort(houses);Arrays.sort(heaters);int ans 0;for (int i 0, j 0; i < houses.length; i) {int curDistance Math.abs(houses[i] - heaters[j]);whil…

【工具篇】MLU运行XInference部署手册

文章目录 前言一、平台环境准备二、代码下载三、安装部署1.正常pip 安装 四、运行结果展示1.如果界面404或没有东西请这样做2.运行效果 前言 Xorbits Inference&#xff08;Xinference&#xff09;是一个功能强大、用途广泛的库&#xff0c;旨在为语言、语音识别和多模态模型提…

Python | Leetcode Python题解之第478题在圆内随机生成点

题目&#xff1a; 题解&#xff1a; class Solution:def __init__(self, radius: float, x_center: float, y_center: float):self.xc x_centerself.yc y_centerself.r radiusdef randPoint(self) -> List[float]:u, theta random.random(), random.random() * 2 * mat…

js简单基础笔记

一 . js特点 1. Js是一门解释型语言&#xff0c;不用编译&#xff0c;而是直接执行 2. js是一门动态语言&#xff0c;其中的任何内容都是不确定的 3. 语法结构和Java&#xff0c;c都很像 4. ​ js是一门面向对象的语言 5.js严格区分大小写 二 . js使用 1…

【AI知识】距离度量和相似性度量的常见算法

本文介绍一些AI中常见的距离度量和相似性度量算法&#xff1a; 1. 欧几里得距离&#xff08;Euclidean Distance&#xff09; 欧几里得距离是最常见的距离度量方法&#xff0c;用来计算两个向量之间的“直线距离”&#xff0c;也被成为L2范数。 公式如下&#xff0c;其中 x…

6、ES6

文章目录 一.关于ES6二.关于变量声明let声明变量const 声明常量 三.变量的解构赋值四.字符串的扩展五.函数的扩展函数默认参数rest参数箭头函数(函数的新写法) 六.数组的扩展七.对象的扩展语法上的简化对象的解构赋值 八.Symbol&#xff1a;新的数据类型(类似于字符串)独一无二…

学习中,师傅b站泷羽sec——xss挖掘过程

某职业技术学院网站xss挖掘&#xff1a; 资产归纳 例如&#xff1a;先把功能点都看一遍&#xff0c;大部分都是文章 根据信息搜集第一课学习到一般主站的防御力是比较强的&#xff0c;出现漏洞的点不是对新手不友好。 在资产验证过程中还是把主站看了一遍 没有发现有攻击的机会…

未来人工智能的发展对就业市场的影响 人工智能在生活中的相关

人工智能&#xff08;Artificial Intelligence&#xff09;&#xff0c;英文缩写为AI.是新一轮科技革命和产业变革的重要驱动力量&#xff0c; 是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门新的技术科学. 人工智能的发展对就业市场的影响主要…

论文笔记:RelationPrompt :Zero-Shot Relation Triplet Extraction

论文来源: ACL Findings 2022 论文链接:https://arxiv.org/pdf/2203.09101.pdf 论文代码:http://github.com/declare-lab/RelationPrompt 本篇论文是由阿里达摩院自然语言智能实验室于2022年发表的关于零样本关系抽取的顶会论文,本篇博客将记录我在阅读过程中的一些笔记…

修改pq_default.ini禁用降噪,解决S905X3电视盒硬解视频画质模糊、严重涂抹得像油画、水彩画的问题

笔者使用一台处理器芯片为 S905X3 的电视盒将近一年&#xff0c;性能比之前的 RK3328 的盒子有所提升&#xff0c;但我对它视频解码方面感到越来越不爽&#xff0c;该盒子的硬解视频总是开启美颜降噪和锐化&#xff0c;导致硬解视频的画质模糊&#xff0c;细节都被磨平&#xf…

使用Jenkins部署项目

部署中的痛点 为什么要用Jenkins&#xff1f;我说下我以前开发的痛点&#xff0c;在一些中小型企业&#xff0c;每次开发一个项目完成后&#xff0c;需要打包部署&#xff0c;可能没有专门的运维人员&#xff0c;只能开发人员去把项目打成一个exe包&#xff0c;可能这个项目已…

OPENSSL-2023/11/10学习记录-C/C++对称分组加密DES

对称分组加密常用算法&#xff1a; DES 3DES AES 国密SM4 对称分组加密应用场景&#xff1a; 文件或者视频加密 加密比特币私钥 消息或者配置项加密 SSL通信加密 对称分组加密 使用异或实现一个简易的对称加密算法 A明文 B秘钥 AB密文AB (AB)B A 密码补全和初始化 数…

第六节——从深层剖析qsort的使用(让你不再害怕指针)

文章目录 1.什么是回调函数2.qsort的使用qsort排序整形数据qsort排序结构体数据qsort排序字符串数据 3.qsort的模拟实现 1.什么是回调函数 回调函数就是⼀个通过函数指针调用的函数。 如果你把函数的指针&#xff08;地址&#xff09;作为参数传递给另⼀个函数&#xff0c;当…

Python画笔案例-087 绘制 旋转的文字

1、绘制 旋转的文字 通过 python 的turtle 库绘制 旋转的文字,如下图: 2、实现代码 绘制 旋转的文字,以下为实现代码: """旋转的文字.py """ import time from turtle import * from write_patch import *screen = Screen