1、多线程编程基本概念,2008,并行硬件,多处理器:MultiprocessorsSMP,Symmetrical Multi-Processing,多路对称技术,要求CPU必是2n个,如2,4,8,16超线程:Hyper threading双核:Dual-Core多核:MulticoreFPGAs:Field Programmable Gate Arrays,多处理器,多处理器指的是多个CPU不在同一个芯片当中1990年代,主要用于服务器,采用刀片式处理器板组合多个CPU现在多处理器都是通过高速通信接口集成在一个主板上,例如:典型的IBM服务器系列MMU:内存管理单元缺点:昂贵,超线程,所谓超线
2、程技术就是利用特殊的硬件指令,把多线程处理器内部的两个逻辑内核模拟成两个物理芯片,从而使单个处理器就能“享用”线程级的并行计算的处理器技术,负责处理第二个线程的那个逻辑处理器,其使用的是仅是运行第一个线程时被暂时闲置的处理单元 当一个逻辑处理器在执行浮点运算(使用处理器的浮点运算单元)时,另一个逻辑处理器可以执行加法运算(使用处理器的整数运算单元)Intel的技术,但AMD和Intel打过超线程的专利官司超线程本质上是一个CPU,目的只不过是提升多线程代码的效率,提升效率约30%P4是典型,双核和多核,多个处理器单元集成在一个芯片当中,典型是2、4、8个多核的问题在于程序编码性能提升最简单的方
3、式:多线程AMD的多核CPU中有一个仲裁机构,Intel的好像没有,双CPU VS. 多核,内存管理Cache通信,FPGAs,现场可编程门阵列用于大规模的硬件设备或适合高性能数值运算,例如DSP应用FPGAs比微处理器运行时钟频率低,并且功耗大FPGA不在我们讨论之列,多任务 Multitasking,多任务OS,如Windows XP多任务是指操作系统能够在多个任务之间快速切换,给人的印象是不同的应用程序正在同时进行,CPU时钟越快,多任务运行也就越流畅单核和多核下的多任务是不一样的单核下的多任务是串行的,多核下的多任务是并行的,多核下的多任务更有效率,多线程 MultiThreading
4、和多核,请问多核下的多线程是否一定就会提升运行效率?,绑定CPU,在多CPU下运行有问题的程序的简单的解决方法:绑定CPU,SetProcessAffinityMask,提升性能的途径,操作系统:操作系统分析指令,对可以并行执行的指令指配到不同CPU,难度较大,目前OS做到可以分配不同线程,还做不到把不同代码段指配道不同CPU编译器:难度较大,目前不现实,编译器智能分析代码,对编译的代码能够支持多核库函数:难度大,有一部分库函数可以利用如Win32线程库,OpenMP,包括Intel提供的相关库函数程序代码:自己编写多线程,重构代码,目标性能提升非常显著,对程序员要求高,针对多CPU的优化例子
5、,functionTest():Boolean; var i:Integer; begin fori:=0to100000do begin /Dosomething. end; end;,讨论?,右边的代码是否可以优化?,X,Y为独立的变量Result := (x *100) + (y / 1234),Amdahl 定理,Amdahl 定理:描述了任意给定代码所能实现加速比的理论可能性 对于串行代码比例占S的代码,理论上可以在 N个处理器上实现的最大加速比为:1/(S+(1-S)/N)。 比如串行代码占20%的代码,在4核处理器上可以实现的最大加速比为1/(20%+(1-20%)/4)=2.5
6、。 推论:加速比受限于代码中串行代码的比例,同步和异步,同步synchronous 和异步asynchronous 阻塞Blocking和非阻塞Non-Blocking串行(serial)和并行(parallel )不同地方同步和异步说法不太一样,但含义一样:同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回 异步就是100个人同时做一件事,同步就是100个人排队做一件事,锁 Lock,冲突:Conflict,对同一个资源的访问冲突解决冲突的方法:锁信号量:Semaphore ,常用,全局临界区:critical section,常用,进程内互斥量:Mutex,常用,全局事件:
7、Event,全局可等待定时器:waitable timer 各个实现的对象,范围不一样,代价也不一样,性能也不一样,多线程和消息处理,Win32中,消息处理过程是运行在窗口创建的时候的线程当中的 *非常重要的一点讨论:有一线程A,在创建的时候Create一个窗口W,另外有一个线程B,在其运行当中,接收到W句柄,然后利用W处理消息,请问如果线程A销毁了,B是否还可以正常运行?,线程池 Thread Pooling,线程创建和销毁是有巨大开销的?为什么?如果需要频繁地、不停创建线程并销毁,那么请使用线程池来解决,优先级,进程优先级(priority class)高于正常,低于正常,极高,空闲,正常
8、,实时线程优先级(priority level )高于正常,低于正常,最高,空闲,最低,正常,实时,总共有16级可以设置线程的优先级同时由进程优先级和线程优先级决定,组合起来总共32级,0(Idle)31(实时)请问:Idle Priority Class的进程中的一个Real Time Thread出现什么情况?那么Real Time Class中的Idle Thread呢?占用CPU最高的是什么?最低的是什么?,优先级,优先级的调整SetThreadPrioritySetPriorityClass优先级使用规则越是高优先级运行的代码,越要简洁,运行时间越短越好周期性的更新的,使用低优先级高
9、优先级用于需要实时响应的功能根据需要临时提升和降低优先级,加锁原则,加锁必须遵循最小原则,即最后关头才Lock,一旦使用完毕,不造成冲突的情况下,立即UnLock自己检查你的设计和代码,很多情况下甚至连锁都可以不需要的,讨论?,有一个全局整数变量G,只有一个线程A,需要修改变量G,另有其他线程T1,T2,T3等,仅读取变量,请问是否需要加锁?如果全局变量为TList或者其他TObject对象,那又如何?原子操作的概念:Atom,瓶颈,现在的技术当中,瓶颈在于内存,I/O总线,硬盘系统编程当中的瓶颈在于对同一个资源的加锁访问减少瓶颈,优化瓶颈可以最有效提升性能,讨论?,如果一段代码,每秒钟可以处
10、理100MB数据,那么在现有的主流硬件条件下,能否通过多CPU和多线程支持进一步提升性能?那么每秒钟20MB的能力又怎么样呢?,多线程程序设计原则,独立的海量计算应该作为独立线程运行独立的数据处理应作为独立线程运行海量I/O应该由同一个线程运行Socket层应该独立一个线程运行线程中尽可能减少堆上的内存分配?请问栈上呢?绝对小心全局变量的使用多线程程序应该在多CPU系统上运行测试,避免单CPU系统对多线程的时序化掩盖问题,多线程编程的常见问题,死锁:死锁是多线程中常见的问题死锁一个是通过调试解决一个是死锁拆除技术调试困难,有时候把IDE都可能挂死,新的IDE一般可以冻结Freeze整个应用程序
11、,所以调试问题还是可以解决的利用Log输出是最简单有效的方式,在加锁和解锁的地方输出一些信息即可利用Process Explorer设定线程,VC的调试器可以直接设定运行线程的状态,死锁例子,数据服务,解码子进程死锁数据服务针对每个RCU创建一个Window,处理消息,收到Socket发送的数据后,通过SendMessage发送消息给子进程窗口Sub,Sub收到消息后,对数据进行处理,解码后发送SendMessage回主进程W,结果死锁,下面代码会有死锁吗?,class TAList FList: TList; procedure Lock; procedure Unlock; procedu
12、re Add(Value: Integer); function GetValue(index: Integer);end;function TAList.GetValue() if (Index 0) and (Index FList.Count) then Result = FListIndex else raise error out of boundsend;procedure TAList.Add(Value: Integer);begin FList.Add(Value);end;,procedure DeakLock(List1, List2: TAList);var V : I
13、nteger; begin List1.Lock; List2.Lock; try V := List.GetValue(10); List2.Add(V); finally List1.UnLock; List2.UnLock; end;end;var FirstList, SecondList: TAList;线程A:DeadLock(FirstList, SecondList);线程B:DeadLock(SecondList, FirstList);,死锁原因,当一段代码需要两个或更多资源时,都有潜在死锁阴影死锁仲裁算法,非常复杂,最好方式是找出一种策略,确保死锁不会发生All-or-N
14、othing策略:要不统统获得,要不统统没有,防止死锁发生讨论:如何解决上一个页面中的死锁问题?,解决方案?,利用Mutex取代Critical Section,配合WaiForMultipleObjects使用修改代码结构再增加临界区?把List1.Lock;List2.Lock作为不可分隔的操作原子?,临界区Lock,请问临界区是否是内核对象?临界区是同一个进程内最常见的进程同步手段在同一个线程当中,可以连续Lock几次,即同线程临界区是可以重入的Lock和UnLock无论何时何地都要保证配对,所以Lock和UnLock一定要用Try Finally,例子,var CR : TRTLCri
15、ticalSection;procedure ThreadProc;begin EnterCriticalSection(CR); try Sleep(5000); finally LeaveCriticalSection(CR); end;end;procedure TForm1.btn1Click(Sender: TObject);var ID: Cardinal;begin FThread := CreateThread(nil, 0, ThreadProc, nil, CREATE_SUSPENDED, ID); ResumeThread(FThread);end;,procedure
16、 TForm1.btn2Click(Sender: TObject);begin EnterCriticalSection(CR);end;procedure TForm1.btn3Click(Sender: TObject);begin LeaveCriticalSection(CR);end;procedure TForm1.FormCreate(Sender: TObject);begin InitializeCriticalSection(CR);end;不对称的Lock/UnLock会导致无休止的等待,题外话:Win32中,内核对象一般要Create,有句柄(Handle)例如Cre
17、ateMutex,CreateEvent,,死锁检测,利用一些API,进行预先检测,例如:TryEnterCriticalSection可以利用一个循环超时检测来判断是否有死锁发生,常见同步API,临界区:DeleteCriticalSection,InitializeCriticalSection,EnterCriticalSection,LeaveCriticalSection,TryEnterCriticalSection事件:CreateEvent,OpenEvent,PulseEvent, ResetEvent,SetEvent 互斥量:CreateMutex ,OpenMutex,
18、ReleaseMutex 信号量:CreateSemaphore ,OpenSemaphore,ReleaseSemaphore 同步等待函数:WaitForSingleObject,WaitForMultipleObjects等,支持的对象包括:Event,Job,Mutex,Process, Semaphore, Thread, Waitable timer等等,讨论?,VCL是如何同步的?,用法示例,创建:FHandle := CreateEvent(EventAttributes, ManualReset, InitialState, PChar(Name);,加锁:WaitForSi
19、ngleObject(Handle, TimeOut);ResetEvent(Handle);解锁SetEvent(Handle);,同步机制摘要,Critical Section:排他性占有,单进程多线程局部对象,非内核对象快速,高效同一时刻只有一个临界区可以等待无法检测是否已经被某个线程放弃Mutex:不同线程(可跨进程)排他性占有内核对象,可以有名字,跨进程Mutex有Owner(拥有者),只能被拥有者释放用Wait等待,Semaphore:追踪有限的资源内核对象,没有拥有者,可以有名字可被任何线程释放Mutex相当于信号量计数为1的SemaphoreEvent:常用于重叠I/O内核对象
20、,可以有名字,跨进程程序完全控制,用于设计新的同步对象“激活”请求可能不会被保存,可能会丢失,利用多CPU进行海量数据处理,线程模型,单线程,利用重叠I/O,尽可能让CPU和磁盘忙碌每个Client一个线程,对于少的Client比较好,缺点:超过1000个Client会死得很难看每个CPU一个线程,可以均衡负载,配合完成端口I/O,完成端口属于另外一个复杂的话题,此处不予讨论,并行计算,并行平台的通信模型: 共享数据(POSIX、windows线程、OpenMP)、消息交换(MPI、PVM) 并行算法模型: 数据并行模型、任务依赖图模型、工作池模型、管理者-工作者模型、消费者模型对于并行计算一个任务可能涉及到的问题: 任务分解、任务依赖关系、任务粒度分配、并发度、任务交互并行算法性能的常见度量值: 并行开销、加速比、效率(加速比/CPU数)、成本(并行运行时间*CPU数),参考资料,Win32多线程程序设计,侯捷翻译,强烈推荐,对多线程编程以及各个概念说明的非常透彻,对各个同步对象有详细描述MSDN:Synchronization Functions,