1、第二章 多线程编程方法综述免费午餐结束了 为了充分利用多个计算核心,提高整个程序的吞吐率,主要是利用多线程1.线程的基本概念2.线程的同步3.多线程编程模型4.多线程编程的原则及要点3内容1.线程的基本概念 进程(早期)进程作为能独立运行的基本单位。(早期)进程作为能独立运行的基本单位。进程是程序在计算机上的一次执行活动。进程是程序在计算机上的一次执行活动。程序是死的(静态的),进程是活的(动态的)。程序是死的(静态的),进程是活的(动态的)。进程是系统进行资源分配和调度运行的一个独立单位进程是系统进行资源分配和调度运行的一个独立单位(也称为任务)。(也称为任务)。引入进程的目的:为了使多个程
2、序并发执行,来改善引入进程的目的:为了使多个程序并发执行,来改善资源利用率及提高系统的吞吐量。资源利用率及提高系统的吞吐量。引入线程的目的:为了减少程序并发执行时所付出的引入线程的目的:为了减少程序并发执行时所付出的时空开销,使操作系统具有更好的并发性。时空开销,使操作系统具有更好的并发性。线程 线程:是进程中的一个实体,是被系统调度和线程:是进程中的一个实体,是被系统调度和分配的基本单元。分配的基本单元。每个程序至少包含一个线程,主线程。每个程序至少包含一个线程,主线程。线程拥有很少的系统资源:程序计数器、一组线程拥有很少的系统资源:程序计数器、一组寄存器和栈寄存器和栈 同一进程所属的各线程
3、共享进程的全部资源同一进程所属的各线程共享进程的全部资源 同一个进程中的多个线程可以并发执行,从而同一个进程中的多个线程可以并发执行,从而更好地改善了资源利用率。更好地改善了资源利用率。1.1 线程与进程的区别 线程是轻量级的进程,是线程是轻量级的进程,是CPUCPU调度和分配的调度和分配的基本单位。基本单位。传统意义的进程是重量级进程。传统意义的进程是重量级进程。代码数据文件寄存器栈线程代码数据文件寄存器栈寄存器栈寄存器栈线程1.1 线程与进程的区别 调度调度 传统的操作系统,进程是传统的操作系统,进程是CPUCPU调度和分配调度和分配的基本单位。的基本单位。引入线程的操作系统,线程是引入线
4、程的操作系统,线程是CPUCPU调度和调度和分配的基本单位,而进程是资源拥有的基本分配的基本单位,而进程是资源拥有的基本单位。单位。进程的两个属性分开,线程轻装运行,可以进程的两个属性分开,线程轻装运行,可以显著提高系统的并发性。显著提高系统的并发性。同一个进程的线程切换简单。同一个进程的线程切换简单。1.1 线程与进程的区别 并发性并发性 进程之间可以并发执行进程之间可以并发执行 一个进程的多个线程之间也可以并发执行一个进程的多个线程之间也可以并发执行 例:未引入线程的操作系统,只有一个文件服例:未引入线程的操作系统,只有一个文件服务进程,它由于某种原因被封锁,就没有其他务进程,它由于某种原
5、因被封锁,就没有其他的文件服务进程来提供服务。若引入线程,一的文件服务进程来提供服务。若引入线程,一个文件服务进程中设置多个服务线程。个文件服务进程中设置多个服务线程。1.1 线程与进程的区别 系统开销系统开销 进程是拥有系统资源的一个独立单位进程是拥有系统资源的一个独立单位 线程不拥有系统资源(只有一点必不可少的线程不拥有系统资源(只有一点必不可少的资源),可以访问其隶属进程的资源。资源),可以访问其隶属进程的资源。一个进程的代码段、数据段以及系统资源一个进程的代码段、数据段以及系统资源(如打开的文件、(如打开的文件、I/OI/O设备等),可供进程设备等),可供进程的所有线程共享。的所有线程
6、共享。1.1 线程与进程的区别 拥有资源拥有资源 创建和撤销进程时,系统都要为之分配和回创建和撤销进程时,系统都要为之分配和回收资源,如内存空间、收资源,如内存空间、I/OI/O设备等。操作系设备等。操作系统所付出的开销将显著大于创建和撤销线程统所付出的开销将显著大于创建和撤销线程时的开销。时的开销。进程切换涉及到当前进程进程切换涉及到当前进程CPUCPU环境的保存环境的保存和新被调度进程的和新被调度进程的CPUCPU环境的设置。环境的设置。线程的切换只需保存少量寄存器的内容,不线程的切换只需保存少量寄存器的内容,不涉及存储器。涉及存储器。1.2 用户级线程、核心级线程和硬件线程 根据多线程实
7、现机制:线程又被分为用户级线程和内核级线程 用户级线程:在用户层通过线程库来实现。对它的创建、撤销和切换都不利用系统的调用。核心级线程:由操作系统直接支持,即无论是在用户进程中的线程,还是系统进程中的线程,它们的创建、撤销和切换都由核心实现。1.2 用户级线程、核心级线程和硬件线程 硬件线程就是线程在硬件执行资源上的表现形式 单个线程一般都包括三个层次:用户级线程映射到核心级线程,再通过硬件相应的接口作为硬件线程来执行。用户级线程和核心级线程之间的映射方式 多对一模型:把多个用户级线程映射到一个核心级线程 一对一模型:把每个用户级线程影射到一个核心级线程 多对多模型:将m个用户级线程影射到n个
8、核心级线程,mn 多对多映射 1.3 线程的生命周期 线程的生命周期:线程从创建到消亡的过程。线程的生命周期:线程从创建到消亡的过程。2 线程的同步 为什么需要线程同步?线程共享同一进程的内存空间 多个线程可能需要同时访问同一个数据 如果没有正确的保护措施,对共享数据的访问会造成数据的不一致和错误。线程线程1 1线程线程2 2i=8i=9i+i-j=ij=i最后的结果取决于线程执行的顺序最后的结果取决于线程执行的顺序常用的线程同步方法 互斥(Mutual exclusion)当一个线程进入某个临界区域(访问线程共享数据的代码段),此时其他想访问这个临界区域的线程就必须等待。临界区(Critic
9、al_Section)信号量(Semaphore)互斥量(Mutex)条件同步(Condition sychronization)执行线程将一直处于阻塞状态直道系统满足指定的条件。条件变量(Conditional Variable)2.1 临界区-互斥 临界区是指包含共享数据的一段代码,这些代码可能临界区是指包含共享数据的一段代码,这些代码可能被多个线程访问或修改。被多个线程访问或修改。当一个线程在临界区内执行的时候,不能有其他任何当一个线程在临界区内执行的时候,不能有其他任何线程被允许在临界区执行。线程被允许在临界区执行。临界区:进入区和退出区临界区:进入区和退出区 进入区进入区退出区退出区
10、临界区临界区2.2 信号量 信号量被定义为一个整数变量 对信号量只能通过两个原子操作P(wait等信号,减量操作)和V(signal给信号,增量操作)signalsignal(S S)S S.value.value;if(Sif(S.value.value=0)=0)Remove a process p from S.L;Remove a process p from S.L;Wakeup(p Wakeup(p););waitwait(S S)S.value S.value-;if if(S S.value.value 0 acquire();/临界区开始 while(LC=true)C-wa
11、it(L);/产生下一个数据 LC=true;C-signal(L);/临界区结束 L-release();void consumer()while(1)L-acquire();/临界区开始 while(LC=false)C-wait(L);/消费下一个数据 LC=false;C-signal(L);/临界区结束 L-release();3.3.多线程的编程模型多线程的编程模型 编程模型:以多线程编程模型为主的并行处理系统和并发式应用程序。多线程编程模型是目前计算机系统架构的最终模型 多线程编程模型的目的,就是“最大限度地利用CPU资源”三种多线程编程模型 流水线 工作组 客户端/服务器3.1
12、 流水线编程模型What Is Pipelining Laundry(洗衣)Example Ann,Brian,Cathy,Dave each have one load of clothes to wash,dry,and fold Washer takes 30 minutes Dryer takes 40 minutes“Folder”takes 20 minutesABCDWhat Is PipeliningSequential laundry takes 6 hours for 4 loadsIf they learned pipelining,how long would laun
13、dry take?ABCD3040 20 3040 20 3040 20 3040 206 PM7891011MidnightTaskOrderTimeWhat Is Pipelining Start work ASAP Pipelined laundry takes 3.5 hours for 4 loads ABCD6 PM7891011MidnightTaskOrderTime3040404040 203.1 流水线编程模型 在流水线方式中,数据元素流串行地被一组线程顺序处理。每个线程反复地在数据系列集上执行同一种操作,并把操作的结果传递给下一步骤的其他线程3.1 流水线编程模型3.2
14、工作组编程模型 数据由一组线程独立地处理 循环的“并行分解”就属于这种模式,通常这种模式称为SIMD并行处理。也可以在不同的数据上执行不同的操作,与MIMD并行处理的定义相似。3.2 工作组编程模型3.3 客户端/服务器方式 客户端请求服务器对一组数据执行某个操作。4.4.多线程编程的原则及要点多线程编程的原则及要点负载平衡 静态负载平衡 人工地将程序分割成多个可并行执行的部分,并保证分割的各个部分能均衡地分布到各个CPU上。动态负载平衡 程序在运行过程中来进行任务的分配达到负载平衡的目的。如:循环的次数是由外部输入决定的 动态负载平衡中对任务的调度一般是由系统来实现的。负载平衡的难题 程序中
15、的可并行执行块很多要靠程序员来划分,CPU核数较少时,划分并不困难。但随着核数增多,划分的粒度将变得越来越细,划分难度增加。负载划分的误差会随着CPU核数的增加而放大。负载划分的难题体现在CPU核数上。负载划分的难题体现在软件升级上。串行化问题 加锁保护导致的串行化问题,如果任务数量固定的前提下,串行化所占的比例是随软件规模的增加而减少的,但不幸的是它会随着任务数量的增加而增加。也就是说处理器个数越多,锁竞争导致的串行化越严重。解决措施 少用锁,甚至采用无锁编程 使用原子操作代替锁,使用原子操作本质上并没有解决串行化的问题,只不过让串行化的速度大大提升,从而使得串行化所占执行时间比例大大下降。从设计和算法层面来缩小串行化所占的比例。从芯片设计方面来考虑的,在芯片层面设计一些新的并行指令
侵权处理QQ:3464097650--上传资料QQ:3464097650
【声明】本站为“文档C2C交易模式”,即用户上传的文档直接卖给(下载)用户,本站只是网络空间服务平台,本站所有原创文档下载所得归上传人所有,如您发现上传作品侵犯了您的版权,请立刻联系我们并提供证据,我们将在3个工作日内予以改正。