现在的位置: 首页 > 综合 > 正文

用户方式中线程的同步——Windows核心编程学习手札之八

2014年02月20日 ⁄ 综合 ⁄ 共 1781字 ⁄ 字号 评论关闭

用户方式中线程的同步

——Windows核心编程学习手札之八

系统中所有线程都必须拥有对各种系统资源的访问权,这些资源包括内存堆栈、串口、文件、窗口和许多其他资源。如果一个线程需要独占对资源的访问权,那么其他线程就无法完成它们的工作;另一方面,也不能让任何一个线程在任何时间都能访问所有的资源。线程在两种情况下需要互相进行通信:1)当有多个线程访问共享资源而不使资源被破坏时;2)当一个线程需要将某个任务已经完成的情况通知另外一个或多个线程时。

线程同步问题在很大程度上与原子访问有关。原子访问是指线程在访问资源时能够确保所有其他线程都不在同一个时间内访问相同的资源。线程同步的原子访问可通过互锁的函数家族来解决,互锁函数的运行取决于运行在何种CPU平台。对于X86家族的CPU来说,互锁函数会对总线发出一个硬件信号,防止另一个CPU访问同一个内存地址;在Alpha平台上,互锁函数要执行下列操作:1)打开CPU中的一个特殊位标志,并注明被访问的内存地址;2)将内存的值读入一个寄存器;3)修改该寄存器;4)如果CPU中的特殊位标志是关闭的,则转入2)步,否则,特殊位标志仍然是打开的,寄存器的值重写入内存。如果系统中的另一个CPU试图修改同一个内存地址,4)步就能够关闭CPU的特殊位标志,从而导致互锁函数返回2)步。无论编译器怎样生成代码,无论计算机安装多少个CPU,它们都能保证以原子操作方式来修改一个值,还必须保证传递给这些函数的变量地址正确地对齐,否则这些函数就会运行失败。调用互锁函数的速度极快(通常会导致执行几个CPU周期,小于50),并且不会从用户方式转换为内核方式(通常需要执行1000CPU周期)。避免 在单个CPU计算机上使用循环锁,如果一个线程正在循环运行,它将会浪费前一个CPU时间,这将防止另一个线程修改该值。循环锁假定,受保护的资源总是被访问较短的时间。这使它能够更加有效地循环运行,然后转为内核方式并进入等待状态。在编程时,如循环运行一定次数(如400次)以上,对资源的访问仍被拒绝,那么该线程就转为内核方式,这种方式下,它要等待(不消耗CPU时间),直到该资源变为可供使用为止。

创建一个能够在多处理器计算机上运行的高性能应用程序,需要了解CPU高速缓存行。 当一个CPU从内存读取一个字节时,它不只是取出一个字节,而是要取出足够的字节来填入高速缓存行。高速缓存行由3264个字节组成(视CPU而定),并且始终在第32个字节或第64个字节的边界上对齐,高速缓存行的作用是为了提高CPU运行的性能,通常情况下,应用程序只能对一组相邻的字节进行处理,如果这些字节在高速缓存中,那么CPU就不必访问内存总线,而访问内存总线需要花费多的时间。但是,在多处理器环境中,高速缓存行使得内存的更新更加困难。

关键代码段是一个小代码段,在代码能够执行前,必须独占对某些共享资源的访问权。这是让若干行代码能够“以原子操作方式”来使用资源的一种方法。所谓原子操作方式,是指该代码知道没有别的线程要访问该资源。当然,系统能够抑制线程的运行,而抢先安排其他线程的运行。在线程退出关键代码之前,系统将不给想要访问相同资源的其他任何线程进行调度。当线程试图进入另一个线程拥有的关键代码段时,调用线程就立即被置于等待状态。这意味着该线程必须从用户方式转入内核方式(大约1000CPU周期)。这种转换是要付出很大代码的。在多处理器上,当前拥有资源的线程可以在不同处理器上运行,并且能够很快放弃对资源的控制。实际上拥有资源的线程可以在另一个线程完成转入内核方式之前释放资源。为提高代码段的运行性能,Microsoft将循环锁纳入了这些代码段,因此,当EnterCriticalSection函数被调用时,它就使用循环锁进行循环,以便设法多次取得该资源,只有当为了取得该资源的每次试图都失败后,该线程才转入内核方式,以便进入等待状态。使用关键的代码段有几个技巧值得学习:1)每个共享资源使用一个CRITICAL_SECTION变量;2)同时访问多个资源,应按照完全相同的顺序请求对资源的访问;3)不可长时间运行关键代码段。

                                 如非

                                  2009-1-4

抱歉!评论已关闭.