《多核程序设计》PPT课件_第1页
《多核程序设计》PPT课件_第2页
《多核程序设计》PPT课件_第3页
《多核程序设计》PPT课件_第4页
《多核程序设计》PPT课件_第5页
已阅读5页,还剩73页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

多核程序设计Multi coreProgramming 提纲 线程多线程编程的问题Windows多线程编程技术 1 多核 多处理器系统中的每颗处理器 每个核 同时间内可以执行各自不同的进程 或线程 2 一颗单线程能力的处理器 一个核 只能执行一个进程 双核处理器就能够同时执行两个不同的进程 或线程 四核就可以同时不同的四个进程 或线程 3 倘若是执行不支持多线程的程序 那么每颗处理器内的多线程功效就无从发挥 4 硬件条件已经具备 但作为人机交互的操作系统和应用软件需要研究它的并行化方法 最大限度挖掘系统的整体性能 多核与多线程编程 多核与多线程编程 发挥软件作用 充分利用系统 CPU 资源 提高使用效率 欲说线程 先从进程 Process 说起 进程是操作系统结构的基础 是一个正在执行的程序 是计算机中正在运行的程序实例 是可以分配给处理器并由处理器执行的一个实体 由单一顺序的执行显示 一个当前状态和一组相关的系统资源所描述的活动单元 进程为应用程序的运行实例 是应用程序的一次动态执行 我们可以简单地理解为 它是操作系统当前运行的执行程序 对应用程序来说 进程就像一个大容器 在应用程序被运行后 就相当于将应用程序装进容器里了 你可以往容器里加其他东西 如 应用程序在运行时所需的变量数据 需要引用的DLL文件等 当应用程序被运行两次时 容器里的东西并不会被倒掉 系统会找一个新的进程容器来容纳它 多线程的概念 线程 thread 是进程上下文 context 中执行的代码序列 是进程中的一个实体 是被系统独立调度和分派的基本单位 又被称为轻量级进程 lightweightprocess 在支持多线程的系统中 进程成为资源分配和保护的实体 而线程是被调度执行的基本单元 线程自己不拥有系统资源 只拥有一点在运行中必不可少的资源 但它可与同属一个进程的其它线程共享进程所拥有的全部资源 线程是程序中一个单一的顺序控制流程 在单个程序中同时运行多个线程完成不同的工作 称为多线程 进程与线程的关系 程序在操作系统中作为进程方式存在 获取资源 运行 在一个进程内 线程可以创建其它线程 每个线程有各自的栈 stack 一个进程内所有的线程共享代码段和数据段 进程 一个进程内的线程示例 对于在一个进程内的线程 一个线程对共享的系统资源进行修改 在这个进程内的其它线程也可以见到这种修改 进程内的多个线程可以对同一个内存单元进行读和写操作 所以必须要采取显式同步机制 在同一个进程的地址空间下 线程间的通信消耗更小 线程的状态 线程的状态就绪 ready 线程等待可用的处理器 运行 running 线程正在被执行 阻塞 blocked 线程正在等待某个事件的发生 比如I O的完成 试图加锁一个被上锁的互斥量 终止 terminated 线程从起始函数中返回或者调用pthread exit 多线程编程的问题 多核与线程并行的关系 在单核平台上的线程并发 在各个核上可以实现线程并行 CPU核 核1 核2 线程的同步 由于线程共享同一进程的内存空间 多个线程可能需要同时访问同一个数据 如果没有正确的保护措施 对共享数据的访问会造成数据的不一致和错误 全局变量是线程间通信最常用的手段 然而却容易造成访问冲突 常用的同步机制 互斥 mutualexclusion 条件同步 conditionsynchronization 线程的本地存储 threadlocalstorage tls 线程同步问题示例 一个银行系统中有两个线程执行取款操作 一个使用ATM机 另一个使用存折在柜台取款 会怎样 如果不加以控制 会使得账户余额为负数 互斥和同步 互斥是指对于共享的操作系统资源 广义的资源 譬如全局变量 在各线程访问时的排它性 临界区 criticalsection 锁 lock 互斥量 mutex 信号量 semaphore 同步是指线程间的一种制约关系 一个线程的执行依赖另一个线程的消息 当它没有得到消息时应等待 直到消息到达时被唤醒 事件 event 信号量 semaphore 临界区 1 示例 intGlobal Sum DWORDWINAPIthreadFunc LPVOIDarg intmySum bigCompute Global Sum mySum return0 voidmain Global Sum 0 for inti 0 i 4 i hThread i CreateThread NULL 0 threadFunc NULL 0 NULL WaitForMultipleObjects 4 hThread TRUE INFINITE 此程序段为包含共享数据Global Sum的一段代码 称为临界区 多个线程同时运行 有可能造成写 写冲突 让一个线程执行这段代码时 其它线程都不能执行 保护起来 怎么办 CRITICAL SECTIONg cs EnterCriticalSection LeaveCriticalSection InitializeCriticalSection DeleteCriticalSection 临界区 2 如果有多个线程试图同时访问临界区 那么在有一个线程进入后其他所有试图访问此临界区的线程将被挂起 并一直持续到进入临界区的线程离开 临界区在被释放后 其他线程可以继续抢占 并以此达到用原子方式操作共享资源的目的 临界区包含两个操作原语 EnterCriticalSection LeaveCriticalSection EnterCriticalSection 语句执行后代码将进入临界区以后无论发生什么 必须确保与之匹配的LeaveCriticalSection 都能够被执行到 否则 临界区保护的共享资源将永远不会被释放 关于临界区的使用 有下列注意点 虽然临界区同步速度很快 但却只能用来同步本进程内的线程 而不可用来同步多个进程中的线程 每个共享资源使用一个CRITICAL SECTION变量 不要长时间运行关键代码段 当一个关键代码段长时间运行时 其他线程就会进入等待状态 这会降低应用程序的运行性能 如果需要同时访问多个资源 则可能连续调用EnterCriticalSection CriticalSection不是OS核心对象 如果进入临界区的线程 挂 了 将无法释放临界资源 这个缺点在Mutex中得到了弥补 锁 互斥量 1 锁 互斥量 跟临界区很相似 只有拥有锁 互斥对象 的线程才具有访问资源的权限 区别是 Mutex所花费的时间比CriticalSection多的多 但是Mutex是核心对象 Event Semaphore也是 可以跨进程使用 而且等待一个被锁住的Mutex可以设定TIMEOUT 不会像CriticalSection那样无法得知临界区域的情况 而一直死等 一个锁最多只能由一个线程获得 因此就决定了任何情况下此共享资源都不会同时被多个线程所访问 任何线程对共享资源进行操作访问前必须先获得锁 否则 线程将保持在该锁地等待队列 直到该锁被释放 当前占据资源的线程在任务处理完后应将拥有的互斥对象交出 以便其他线程在获得后得以访问资源 如果一个拥有Mutex的线程在返回之前没有调用ReleaseMutex 那么这个Mutex就被舍弃了 但是当其他线程等待 WaitForSingleObject等 这个Mutex时 仍能返回 并得到一个WAIT ABANDONED 0返回值 能够知道一个Mutex被舍弃是Mutex特有的 锁 2 示例 ThreadAvoidsome method printf Ahelloone printf Ahellotwo ThreadBvoidsome method printf Bhelloone printf Bhellotwo 可能的输出结果 Ahelloone Bhellotwo Bhelloone Ahellotwo 怎么办 能控制线程次序么 哪个线程捕获到锁哪个运行 其它线程等待 Mutexmutex 定义一个锁变量 mutex lock 获取锁 mutex unlock 释放锁 mutex lock 获取锁 mutex unlock 释放锁 Ahelloone Ahellotwo Bhelloone Bhellotwo Bhelloone Bhellotwo Ahelloone Ahellotwo 锁 3 互锁怎么办 互锁问题的产生生产者 消费者问题若消费者线程先捕获了锁 一种解决途径条件变量 信号量 semaphore 1 历史最悠久的同步方法Dijkstra在1965年提出PV操作 原子操作 信号量是解决producer consumer问题的关键要素 意义信号量Semaphore是一个整数值 代表目前可用的资源数 系统同步初始可以设有多少资源 即可以将信号量初值设为0到某个最大值之间的任意数 如果Semaphore的现值为1 表示还有一个锁定动作可以成功 如果现值为5 就表示还有五个锁定动作可以成功 信号量状态在其计数 0时有信号 即当前资源的数量 0 信号量有效 0时无信号 即当前资源的数量是0 信号量无效 系统不允许当前资源的数量为负值 即不能在信号量为负的情况下执行任务 V操作时也不能超过最大值 核心对象 可以跨进程访问 信号量 2 示例 保护共享资源 DWORDWINAPIthreadFunc LPVOIDarg intmySum bigCompute Global Sum mySum return0 DWORDWINAPIthreadFunc LPVOIDarg intmySum bigCompute Global Sum mySum return0 thread1 thread2 Semaphoresem 1 创建信号量变量sem 并设其初值为1 P sem 若sem 0 sem减1 P sem 若sem 0 sem减1 V sem sem加1并判断 V sem sem加1并判断 信号量已经小于等于0的线程会进入等待队列 关键在于P sem 时 即减和判断期间 不允许其他线程打断 即原子操作 信号量 3 示例 同步线程 生产者消费者问题 thread producer LPVOIDarg intmySum1 bigCompute Global Sum mySum1 return0 thread Consumer LPVOIDarg intmySum2 mySum2 Global Sum return0 thread1 thread2 Semaphoresem 0 创建信号量变量sem 并设其初值为0 P sem 若sem 0 sem减1 V sem sem加1并判断 运算次序 mySum2 Global Sum mySum1 mySum2 事件 Event 1 作用 同步线程用事件 Event 来同步线程最具弹性 灵活 一个事件有两种状态 激发状态和未激发状态 也称有信号状态和无信号状态 事件类型手动重置事件手动重置事件被设置为激发状态后 会唤醒所有等待的线程 而且一直保持为激发状态 直到程序重新把它设置为未激发状态 自动重置事件自动重置事件被设置为激发状态后 会唤醒 一个 等待中的线程 然后自动恢复为未激发状态 所以用自动重置事件来同步两个线程比较理想 事件 2 示例 读 写 线程同步 VoidReadThread LPVOIDp cout GlobalSum endl VoidWriteThread LPVOIDp GlobalSum intmain evRead CreateEvent NULL FALSE FALSE NULL evFinish CreateEvent NULL FALSE FALSE NULL beginthread ReadThread 0 NULL beginthread WriteThread 0 NULL WaitForSingleObject evFinish INFINITE WaitForSingleObject evRead INFINITE SetEvent evFinish SetEvent evRead HANDLEevRead evFinish 线程的本地存储 TLS TLS为每个线程维护一个与该线程绑定的变量的副本 从而隔离了多个线程的数据 JAVA中的支持Java lang ThreadLocal 并行 多线程 程序设计方法 显式线程 Explicitthreading 微软Windows线程API Pthreads Java线程类等 利用编译器指导 Compiler directed 自动并行 OpenMP IntelThreadingBuildingBlocks等利用并行应用库 Parallellibraries IntelIPP MKL ScaLAPACK PARDISO PLAPACK并行程序语言有150种以上消息传递 Messagepassing MPI OpenMP PVM 等等 串行程序并行化 多线程 一般流程 分析VTune PerformanceAnalyzer可并行性分析设计 IntroduceThreads Intel Performancelibraries IPPandMKLOpenMP Intel Compiler Explicitthreading Win32 Pthreads 调试错误Intel ThreadCheckerIntel Debugger性能分析和调整Intel ThreadProfilerVTune PerformanceAnalyzer Windows环境下的多线程编程 Windows线程库介绍 Win32APIWindows操作系统为内核以及应用程序之间提供的接口 将内核提供的功能进行函数封装 应用程序通过调用相关的函数获得相应的系统功能 MFC微软基础函数类库 MicrosoftFoundationClasses 由微软提供的 用类库的方式将Win32API进行封装 以类的方式提供给开发者 AfxBeginThread Threadfunc hWnd THREAD PRIORITY NORMAL Windows线程库介绍 NETFramework由两部分构成 公共语言运行库 CommonLanguageRuntime CLR 和Framework类库 FrameworkClassLibrary FCL NET基础类库的System Threading命名空间提供了大量的类和接口来支持多线程 所有与多线程机制相关的类都存放在System Threading命名空间中 使用win32线程API Win32函数库中提供了操作多线程的函数 包括创建线程 管理线程 终止线程 线程同步等接口 线程函数DWORDWINAPIThreadFunc LPVOIDlpvThreadParm 线程创建HANDLECreateThread LPSECURITY ATTRIBUTESlpThreadAttributes SIZE TdwStackSize LPTHREAD START ROUTINElpStartAddress LPVOIDlpParameter DWORDdwCreationFlags LPDWORDlpThreadId 也可以用 beginthread 多线程函数名 0 NULL 须引入 process h 使用win32线程API HANDLE Windows中的句柄实际上是一个唯一的数字 它引用一个Windows对象 例如窗口或者图标等 句柄是一种指向指针的东西 我们知道 所谓指针是一种内存地址 应用程序启动后 组成这个程序的各对象是驻留在内存的 如果简单地理解 似乎我们只要获知这个内存的首地址 那么就可以随时用这个地址访问对象 但是 如果真的这样认为 那么就大错特错了 Windows是一个以虚拟内存为基础的操作系统 在这种系统环境下 Windows内存管理器经常在内存中来回移动对象 依此来满足各种应用程序的内存需要 对象被移动意味着它的地址变化了 如果地址总是如此变化 我们该到哪里去找该对象呢 为了解决这个问题 Windows操作系统为各应用程序腾出一些内存储地址 用来专门登记各应用对象在内存中的地址变化 而这个地址 存储单元的位置 本身是不变的 可以简单的把句柄看邮箱 即使住处变了 由于邮箱不变 人们仍然可以寄信 邮局记录这些句柄的对应关系 使用win32线程API HANDLE Windows内存管理器在移动对象在内存中的位置后 把对象新的地址告知这个句柄地址来保存 这样我们只需记住这个句柄地址就可以间接地知道对象具体在内存中的哪个位置 这个地址是在对象装载 Load 时由系统分配给的 当系统卸载时 Unload 又释放给系统 句柄地址 稳定 记载着对象在内存中的地址 对象在内存中的地址 不稳定 实际对象但是 必须注意的是程序每次重新启动 系统不能保证分配给这个程序的句柄还是原来的那个句柄 而且绝大多数情况的确不一样的 假如我们把进入电影院看电影看成是一个应用程序的启动运行 那么系统给应用程序分配的句柄总是不一样 这和每次电影院售给我们的门票总是不同的一个座位是一样的道理 使用WIN32线程API 线程函数 MyThreadStart LPVOIDp 即为线程的执行函数 线程函数 CreateThread的参数 lpThreadAttributeThisisoptionalsecurityforchildprocesses ItcanbeNULL dwStackSizeThisisstacksizeinbytes Itcanbe0 whichmeansusedefault usually1megabyte lpStartAddressThisisagloballyvisiblefunctiondeclaredDWORDWINAPI Thisisthefunctionforthethreadtobeginexecution lpParameterThisisapointertotheoneparameterfor lpStartAddress function Useapointertoastructuretopassmultipleparameters dwCreationFlagsThiscreatesathreadandstartsorsuspendsit Use0tostart otherwiseuseCREATE SUSPENDED lpThreadIdThisisanoutputparameterandreturnsaunique acrossthesystem integerforthethread ItcanbeNULL 线程的管理 设置线程的优先级线程优先级 进程优先级 线程相对优先级BoolSetThreadPriority HANDLEhThread intnPriority 线程的挂起与恢复进程中的每个线程都有挂起计数器 suspendcount 当挂起计数器值为0时 线程被执行 当挂起计数器值大于0时 调度器不去调度该线程 DWORDSuspendThread HANDLEhThread DWORDResumeThread HANDLEhThread 线程终结 VOIDExitThread DWORDdwExitCode 线程的等待和退出 线程间的等待一组能使线程阻塞其自身执行的等待函数WaitForSingleObject WaitForMultipleObject 释放操作系统资源在程序结束前要清除线程及其所占资源线程退出调用函数 BOOLCloseHandle HANDLEhObject 多线程基础 示例分析 多线程基础 示例分析 方案一 WaitingforaThread Waitforoneobject thread Callingthreadwaits blocks until Timeexpires Returncodeusedtoindicatethis Threadexits handleissignaled UseINFINITEtowaituntilthreadterminationDoesnotuseCPUcycles WaitingforManyThreads Waitforupto64objects threads Waitforall fWaitAll TRUEWaitforany fWaitAll FALSEReturnvalueisfirstarrayindexfound Example MultipleThreads Modifythepreviousexamplecodetoprintout Appropriate HelloThread message Uniquethreadnumber Usefor loopvariableofCreateThreadloopSampleoutput Example2 HelloThreads Example2 HelloThreads What swrong WhatisprintedformyNum HelloThreadsTimeline 程序中出现了 数据竞争 softerror 数据竞争 RaceConditions Concurrentaccessofsamevariablebymultiplethreads Read Writeconflict Write WriteconflictMostcommonerrorinconcurrentprogramsMaynotbeapparentatalltimes HowtoAvoidDataRaces Scopevariablestobelocaltothreads Variablesdeclaredwithinthreadedfunctions Allocateonthread sstack TLS ThreadLocalStorage Controlsharedaccesswithcriticalregions Mutualexclusionandsynchronization Lock semaphore event criticalsection mutex 程序修改方案Solution Local Storage 多线程编程举例 示例一 exa1 cpp function beginthread fun 0 NULL include stdafx h include include include includeusingnamespacestd 保证标准屏幕输入 输出语句的使用voidThreadFunc1 PVOIDparam while 1 Sleep 1000 cout ThisisThreadFunc1 endl voidThreadFunc2 PVOIDparam while 1 Sleep 1000 cout ThisisThreadFunc2 endl intmain 主函数 inti 0 Shouldbeaddinmaincodebody ifdef INTEL COMPILERprintf INTEL COMPILER d n INTEL COMPILER elseprintf INTEL COMPILERisnotdefined n endif beginthread ThreadFunc1 0 NULL beginthread ThreadFunc2 0 NULL Sleep 3000 cout end endl return0 多线程编程举例 示例一 include includeusingnamespacestd DWORDWINAPIFunOne LPVOIDparam while true Sleep 1000 cout hello return0 DWORDWINAPIFunTwo LPVOIDparam while true Sleep 1000 cout world return0 多线程编程举例 示例二 程序首先创建两个线程 一个线程打印hello一个线程打印world当输入为1时 执行线程否则挂起线程 intmain intargc char argv intinput 0 HANDLEhand1 CreateThread NULL 0 FunOne void 多线程编程举例 示例二 include stdafx h include includeusingnamespacestd intglobalvar false DWORDWINAPIThreadFunc LPVOIDpParam cout ThreadFunc endl Beep 5000 2000 beepandSleep 200 globalvar true return0 intmain 首先创建新线程HANDLEhthread CreateThread NULL 0 ThreadFunc NULL 0 NULL if hthread cout ThreadCreateError endl CloseHandle hthread while globalvar cout Threadwhile endl 主线程运行cout Threadexit endl return0 多线程编程举例 示例三 exa3 cpp CreateThread函数及全局变量的使用 线程执行和资源存取 线程之间通信的两个基本问题是互斥和同步线程同步是指线程之间所具有的一种制约关系 一个线程的执行依赖另一个线程的消息 当它没有得到另一个线程的消息时应该等待 直到消息到达时才被唤醒 线程互斥是指对于共享资源 在各线程访问时的排它性 Win32线程同步的实现全局变量事件 Event 临界区 Criticalsection 互斥量 Mutex 信号量 Semaphore Win32线程同步的实现 全局变量进程中的所有线程均可以访问所有的全局变量 因而全局变量成为Win32多线程通信的最简单方式 事件事件 Event 是WIN32提供的最灵活的线程间同步方式 事件存在两种状态 激发状态 signaledortrue 未激发状态 unsignalorfalse 事件可分为两类 手动设置 这种对象只能用程序来手动设置 在需要该事件或者事件发生时 采用SetEvent及ResetEvent来进行设置 自动恢复 一旦事件发生并被处理后 自动恢复到没有事件状态 不需要再次设置 Win32线程同步的实现 续 临界区一种防止多个线程同时执行一个特定代码段的机制适用于多个线程操作之间没有先后顺序但要求互斥的同步 多个线程访问同一个临界区的原则 一次最多只能一个线程停留在临界区内不能让一个线程无限地停留在临界区内 否则其他线程将不能进入该临界区互斥量通常用于协调多个线程或进程的活动 通过 锁定 和 取消锁定 资源 控制对共享资源的访问 信号量信号量是一个核心对象 拥有一个计数器 可用来管理大量有限的系统资源 当计数值大于零时 信号量为有信号状态 当计数值为零时 信号量处于无信号状态 Win32线程同步的实现 全局变量 使用全局变量保存线程中的关键数据或信息 Win32线程同步的实现 事件 Event Usedtosignalotherthreadsthatsomeeventhasoccurred Dataisavailable messageisreadyThreadswaitforsignalwithWaitFor functionTwokindsofevents Auto reset Manual reset Caution BecarefulwhenusingWaitForMultipleObjectstowaitforALLevents Win32线程同步的实现 事件 Event SetbManualResettoTRUEformanual resetevent FALSEforauto reseteventSetbInitialStatetoTRUEforeventtobegininsignaledstate FALSEtobeginunsignaled Win32线程同步的实现 事件 Event Win32线程同步的实现 事件 Win32线程同步的实现 CriticalSection Newtype CRITICAL SECTIONcs Createanddestroyoperations InitializeCriticalSection Win32线程同步的实现 CriticalSection Win32线程同步的实现 Mutexes Mutex对象的状态在它不被任何线程拥有时才有信号 而当它被拥有时则无信号 Mutex对象很适合用来协调多个线程对共享资源的互斥访问 可按下列步骤使用该对象 首先 建立互斥体对象 得到句柄 HANDLECreateMutex 然后 在线程可能产生冲突的区域前 即访问共享资源之前 调用WaitForSingleObject 将句柄传给函数 请求占用互斥对象 dwWaitResult WaitForSingleObject hMutex 5000L 共享资源访问结束 释放对互斥体对象的占用 ReleaseMutex hMutex 互斥体对象在同一时刻只能被一个线程占用 当互斥体对象被一个线程占用时 若有另一线程想占用它 则必须等到前一线程释放后才能成功 Win32线程同步的实现 Mutexes Win32线程同步的实现 Mutexes 续 Win32线程同步的实现 Semaphore 信号对象允许同时对多个线程共享资源进行访问 在创建对象时指定最大可同时访问的线程数 当一个线程申请访问成功后 信号对象中的计数器减一 调用ReleaseSemaphore函数后 信号对象中的计数器加一 其中 计数器值大于或等于 但小于或等于创建时指定的最大值 如果一个应用在创建一个信号对象时 将其计数器的初始值设为 就阻塞了其他

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论