理解计算机系统_线程(八):并行

news2025/5/26 6:42:28

前言
       

        以<深入理解计算机系统>(以下称“本书”)内容为基础,对程序的整个过程进行梳理。本书内容对整个计算机系统做了系统性导引,每部分内容都是单独的一门课.学习深度根据自己需要来定

引入

        接续理解计算机系统_并发编程(10)_线程(七):基于预线程化的并发服务器-CSDN博客,理解并行

顺序、并发和并行的概念(联系与区别)

        本书P711图12-30

        本书原话:所有程序的集合能够被划分成不相交的顺序程序集合和并发程序的集合.写顺序程序只有一条逻辑流.写并发程序有多条并发流.并行程序是一个运行在多个处理器上的并发程序.因此,并行程序的集合是并发程序集合的真子集.

        用白话来理解这几个概念:

        首先通用部分:程序是任务,在机器层面是指令流.CPU是完成任务的工具.

        CPU的运行机制:在多任务系统中(通常非单片机都属于多任务系统),CPU面对的是多个同时加载进内存的任务(进程),CPU按照"时间片+中断"在进程中做切换---一般情况下不是CPU执行完一整个任务再去执行下一个任务,而是每次执行任务的一部分,按照某种条件(比如时间已到,或者硬件中断,或者信号)转去执行下一个任务.

        顺序程序:一条逻辑(指令)流

        并发程序:多条并发(指令)流,通常指对应单个CPU

        并行程序:多条并发(指令)流,通常指对应多个CPU

意义:将顺序程序转为并发(或并行)程序,可以提高效率.

=============================内容分割线↓===================================

思考:CPU调度算法如何实现?

        笔者做了尝试.

        第一步,调度要操作的是什么?线程.而线程抽象为函数Thread.

/*声明函数指针thread,将其设置为一个类型*/
typedef void* (*Thread)(void * arg);

        然后所有的函数都化成Thread对象.---可以做到,如果有多个形参,放到一个结构里,返回值的void*可以不用,也可以放到结构里,前面帖子有举例. 

        第二步,建立线程集合.选用什么样的数据结构?动态数组或者二叉树还是其他哪一种?这里需要经验或者代码验证了,不展开叙述.简单用动态数组.

//线程的集合
Thread* threads;      //线程的动态数组
int n;                //线程个数

        显然这里还需要增删查的算法,不展开了. 

        /*到每次写和数据结构有关算法的时候,就念起C++的好处来了,不用每次都重新写*/ 

         第三步,写调度算法.

         函数文字表达:从第一个线程开始,每当时间片到达或者信号到达或者硬件中断时,退出当前执行的线程,记录线程上下文并跳转到下一个线程.线程集合假设在执行完毕之后刷新

         这里需要汇编语言,做一些假设:

         假设程序计数器PC可以直接用,每个线程被编译成机器指令的集合.有一个机器指令的指针--假设为MP(类型和PC一样)指向当前线程,他是可以++的.每个线程的末尾有标识.

//调度算法,伪代码
void dispatch(int n,Thread* threads){
    线程上下文回溯;
    int time=当前时间;                        /*调用系统内核中计算current_time的函数*/
    while(time==当前时间+时间片大小)||信号到达||硬件中断||PC没指向线程末尾){
        PC=MP++;                             /*执行机器代码,如果while条件不成立,指针+1*/
    }

    if(信号)
        执行信号函数;

    if(硬件中断)
        执行硬件中断函数;

    if(线程未结束)
        保存线程上下文;
        添加进一个新的线程集合;


    //不管线程有没有结束,都跳转并返回
        dispatch(++n,threads);               /*跳转执行线程集合中下一条*/
        return;

}

        这是一个很简陋的算法,基本思路是一边执行线程,一边更新一个新的线程集合,把未执行完毕的线程放进去. 需要考虑的东西还很多,比如:

        时间片大小是怎么确定?是否在不同场景采用不同的设置

        信号,硬件中断的函数如何设计?可能还要考虑中断嵌套

        线程优先级如何安排(线程排序)?---某些重要线程要排在前面,执行2次,其他非重要线程执行1次

        新的线程集合threads什么时候更新?--添加新线程

        在<操作系统概论>里,有专门的篇章介绍进程调度的算法---提出了多种模型(但未实现,需要系统级的汇编语言支持),进程调度在整个操作系统设计里,占的内容不少,这里只是做一些设想,并不做准确的保证.

        以上内容是为锻炼思维的一点思考

=============================内容分割线↑===================================

并行程序分析

        本书P711举了高斯数列的例子.本书P711第4段:将任务分配到不同线程的最直接方法是将序列划分为t个不相交的区域,然后给t个不同的线程每个分配一个区域.为了简单,假设n是t的倍数,这样每个区域有n/t个元素.

        ---解读:使用并发(并行)对程序效率的提高,对程序员来说很有吸引力.并发(并行)的前提是:各个线程之间是独立的,不存在因果关系(如线程A必须先于线程B完成,或者说线程B用到了线程A执行后的结果)

多线程解高斯数列求和

思路:有连续的n个数字,划分为t块,每块的数据为n/t个,每块用一个线程函数求解.

思路图

 最简单也最直接的选择是将线程的和放入一个共享全局变量中,用互斥锁保护这个变量.

        ---解读:第一种方案:一个共享全局变量.

第一种方案的代码解读

        主函数第21~25行

nthreads=atoi(argv[1]);            //传入线程个数
log_nelems=atoi(argv[2]);          //传入幂
nelems=(1L<<log_nelems);           //元素总个数
nelems_per_thread=nelems/nthreads; //每线程元素个数
sem_init(&mutex,0,1);              //信号量初始化

        注意:这里计算的总个数,是自己定的,始终保持2的n次幂,传入的log_nelems就是幂的值.这样做的目的前面讲了是为了方便计算.实际情况不能自己定计算个数,就算是1000这种非2的n次幂值,问题也不大.

        主函数第27~33行

        线程建立及线程完毕后资源回收

        线程函数  

        ---每次迭代都更新共享全局变量gsum.注意,我们很小心地用P和V互斥操作来保护每次更新 

        注意:第5行求出每线程首个元素的大小.元素大小和块序号myid有关.而myid来自主函数第29行.这里得到的启示是:当需要的数据没找到时,在全局变量或者main函数的局部变量中声明,再行定义.代码中从前到后是这样写的:

        主函数中:

                第13行声明:long myid[MAXTHREADS];---声明一个long数组,存放序号

                第29行,每生成一个线程,就把序号放到myid数组中

                第30行,将序号传入线程函数:Pthread_create的第4个参数:&myid[i]

        线程函数中:

                第4行:取出序号

                第5行:使用序号得到每个线程首元素的值.

=============================内容分割线↓===================================

        写代码的时候,常常有这种情况:找不到想要的数据.例如笔者初始不知道怎么得到每线程首元素值.解决办法可以参照本书代码:从前面声明,用产生数据的逻辑放到应该的位置.

        思路清晰,总是能找到解决办法.

=============================内容分割线↑===================================

 第一种方案失败原因分析

        本书P713的图,第一种方案的结论:运行时间很长,性能差,而且使用核数越多,性能越差.

        本书原话:造成性能差的原因是相对于内存更新操作的开销,同步操作(P和V)代价太大.这突显了并行编程的一项重要教训:同步开销巨大,要尽可能避免.如果无可避免,必须要用尽可能多的有用计算弥补这个开销.

第二种方案:避开同步

        本书P713第3段:一种避免同步的方法是让每个对等线程在一个私有变量中计算他自己的部分和,这个私有变量不与其他任何线程共享,主线程定义一个全局数组psum,每个对等线程i把他的部分和累积在psum[i]中.因为小心地给了每个对等线程一个不同地内存位置来更新,所以不需要用互斥锁来保护这些更新.唯一需要同步的地方是主线程必须等待所有的子线程完成.在对等线程结束后,主线程把psum向量的元素加起来,得到最终的结果.

        ---代码如图

        全局数组psum定义在第10行之前:

long psum[MAXTHREADS];

         与之对应:

         第21行的nthreads,传入的线程数必须少于MAXTHREADS.

         主函数的最后要用一个for循环把psum数组的值加起来得到结果.

第二种方案的优化

        本书原话:第5章中,我们学习到了如何使用局部变量来消除不必要的内存引用.

        ---技巧:即使是在函数内部中要使用全局变量,也尽量不要频繁使用.代码如图

        计算后的结果赋值给全局变量psum[myid],而不是第二种方案优化前的for循环中每次都访问他 

并行效率等问题

        作为程序员来说,并行肯定能用则用,效率问题主要集中在选几个核参加并行得到的效果更好.笔者认为这些内容太底层了暂不考虑.本书原话:数十年来,并行编程一直是一个很活跃的研究领域.随着商用多核机器的出现,这些机器的核数每几年就翻一番,并行编程会继续是一个深入、苦难而活跃的研究领域.

小结

        用本书中高斯数列求和的例子,理解了并行编程."挑战"了调度算法;总结编程时"找数据"的思路.

        并发(并行)编程的前提条件:线程之间不相交.尽量使用并发(并行)提高运行效率.

       

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

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

相关文章

【MySQL】09.索引

索引是用来提高数据库的性能的&#xff0c;但查询速度的提高是以插入、更新、删除的速度为代价的&#xff0c;这些写操作&#xff0c;增加了大量的IO。所以它的价值在于提高一个海量数据的检索速度。 1. 认识磁盘 MySQL 给用户提供存储服务&#xff0c;而存储的都是数据&…

【备忘】 windows 11安装 AdGuardHome,实现开机自启,使用 DoH

windows 11安装 AdGuardHome&#xff0c;实现开机自启&#xff0c;使用 DoH 下载 AdGuardHome解压 AdGuardHome启动 AdGuard Home设置 AdGuardHome设置开机自启安装 NSSM设置开机自启重启电脑后我们可以访问 **http://127.0.0.1/** 设置使用 AdGuardHome DNS 效果图 下载 AdGua…

[Windows] 游戏常用运行库- Game Runtime Libraries Package(6.2.25.0409)

游戏常用运行库 合集 整合了许多游戏会用到的运行库&#xff0c;支持 Windows XP – Windows 11 系统&#xff0c;并且支持自动检测系统勾选推荐的运行库&#xff0c;方便快捷。 本版特点&#xff1a; By&#xff1a;mefcl 整合常见最新游戏所需运行库 根据系统自动勾选推荐…

(九)PMSM驱动控制学习---高阶滑膜观测器

在之前的文章中&#xff0c;我们介绍了永磁同步电机无感控制中的滑模观测器&#xff0c;但是同时我们也认识到了他的缺点&#xff1a;因符号函数带来的高频切换分量&#xff0c;使用低通滤波器引发相位延迟&#xff1b;在本篇文章&#xff0c;我们将会介绍高阶滑模观测器的无感…

25年上半年五月之软考之设计模式

目录 一、单例模式 二、工厂模式 三、 抽象工厂模式 四、适配器模式 五、策略模式 六、装饰器模式 ​编辑 考点&#xff1a;会挖空super(coffeOpertion); 七、代理模式 为什么必须要使用代理对象&#xff1f; 和装饰器模式的区别 八、备忘录模式 一、单例模式 这个…

Mongo DB | 多种修改数据库名称的方式

目录 方法一&#xff1a;使用 mongodump 和 mongorestore 命令 方法二&#xff1a;使用 db.copyDatabase() 方法 方法三&#xff1a;使用 MongoDB Compass 在 MongoDB 中&#xff0c;更改数据库名称并不是一个直接的操作&#xff0c;因为 MongoDB 不提供直接重命名数据库的命…

QListWidget的函数,信号介绍

前言 Qt版本:6.8.0 该类用于列表模型/视图 QListWidgetItem函数介绍 作用 QListWidget是Qt框架中用于管理可交互列表项的核心组件&#xff0c;主要作用包括&#xff1a; 列表项管理 支持动态添加/删除项&#xff1a;addItem(), takeItem()批量操作&#xff1a;addItems()…

EtherNet/IP机柜内解决方案在医疗控制中心智能化的应用潜能和方向分析

引言 在数智化转型浪潮席卷各行各业的今天,医疗领域同样面临着提升运营效率、改善患者体验和加强系统可靠性的多重挑战。Rockwell Automation于2025年5月20日推出的EtherNet/IP机柜内解决方案,为医疗中心的自动化升级提供了一种创新路径。本报告将深入分析这一解决方案的核心…

【结构设计】以3D打印举例——持续更新

【结构设计】以立创EDA举例——持续更新 文章目录 [TOC](文章目录) 前言立创EDA官网教程一、3D外壳绘制二、3D外壳渲染三、3D外壳打印1.3D打印机——FDM2.3D打印机——光固化 四、3D外壳LOG设计1.激光雕刻机 总结 前言 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面…

MySQL中的重要常见知识点(入门到入土!)

基础篇 基础语法 添加数据 -- 完整语法 INSERT INTO 表名 (字段名1, 字段名2, ...) VALUES (值1, 值2, ...);-- 示例 insert into employee(id,workno,name,gender,age,idcard,entrydate) values(1,1,Itcast,男,10,123456789012345678,2000-01-01) 修改数据 -- 完整语法 UPDA…

理解全景图像拼接

1 3D到2D透视投影 三维空间上点 p 投影到二维空间 q 有两种方式&#xff1a;1&#xff09;正交投影&#xff0c;2&#xff09;透视投影。 正交投影直接舍去 z 轴信息&#xff0c;该模型仅在远心镜头上是合理的&#xff0c;或者对于物体深度远小于其到摄像机距离时的近似模型。…

云原生安全基石:Linux进程隔离技术详解

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 进程隔离是操作系统通过内核机制将不同进程的运行环境和资源访问范围隔离开的技术。其核心目标在于&#xff1a; 资源独占&#xff1a;确保…

基于PySide6与pycatia的CATIA几何阵列生成器开发实践

引言:参数化设计的工业价值 在航空航天、汽车制造等领域,复杂几何图案的批量生成是模具设计与机械加工的核心需求。传统手动建模方式存在效率低下、参数调整困难等问题。本文基于PySide6+pycatia技术栈,实现了一套支持​​动态参数配置​​、​​智能几何阵列生成​​的自动…

Linux学习心得问题总结(三)

day09 文件权限篇 文件权限的属性有哪些&#xff1f;我们应如何理解这些属性&#xff1f; 文件权限的属性包括可读&#xff08;r&#xff09;、可写&#xff08;w&#xff09;、可执行&#xff08;x&#xff09;三种权限&#xff0c;根据文件类型可分为普通文件&#xff08;.…

Anthropic推出Claude Code SDK,强化AI助理与自动化开发整合

Anthropic发布Claude Code SDK&#xff0c;协助开发团队将人工智慧助理整合进自动化开发流程&#xff0c;支援多轮对话、MCP协定及多元格式。 Anthropic推出Claude Code SDK&#xff0c;提供开发者与企业一套可程序化整合Claude AI助理至开发流程的工具。此SDK以命令列介面为基…

6.4.1最小生成树

知识总览 生成树(一定是连通的)&#xff1a; 是连通的无向图的一个子图&#xff0c;子图包含这个无向图的所有顶点有n-1条边(少一条边&#xff0c;生成树就不连通了)即为生成树&#xff0c;一个连通图可能有多个生成树 最小生成树(最小代价树)&#xff1a; 只有连通的无向图才…

DARLR用于具有动态奖励的推荐系统的双智能体离线强化学习(论文大白话)

1. 概述 离线强化学习是现在强化学习研究的一个重点。相比与传统的强化学习它不需要大量的实时交互数据&#xff0c;仅仅依赖历史交互日志就可以进行学习。本文就是将离线强化学习用于推荐系统的一篇文章。 这篇文章主要解决的核心问题有以下几个&#xff1a; 1&#xff09;…

第35节:PyTorch与TensorFlow框架对比分析

引言 在深度学习领域,PyTorch和TensorFlow无疑是当前最受欢迎的两大开源框架。 自2015年TensorFlow由Google Brain团队发布,以及2016年Facebook的AI研究团队推出PyTorch以来,这两个框架一直在推动着深度学习研究和工业应用的发展。 本文将从多个维度对这两个框架进行详细对…

企业级智能体 —— 企业 AI 发展的下一个风口?

在AI技术迅猛发展的当下&#xff0c;企业对AI的应用不断深入。企业级智能体逐渐受到关注&#xff0c;它会是企业AI发展的下一个风口吗&#xff1f;先来看企业典型的AI应用场景&#xff0c;再深入了解企业级智能体。 企业典型AI应用场景 1. 内容生成&#xff1a;2025年&#xf…

【软考向】Chapter 2 程序设计语言基础知识

程序设计语言概述低级语言 —— 机器指令、汇编语言高级语言 ——翻译:汇编、解释和编译语言处理程序基础 —— 翻译给计算机,汇编、编译、解释三类编译程序基本原理 —— 词法分析、语法分析、语义分析、中间代码生成、代码优化、目标代码生成文法和语言的形式描述确定的有限…