Electronic Joint Business

Solution for E-Business

synchronization

Windows 下的多线程同步机制

广义上的进程是程序在计算机上的一次执行活动。当运行一个程序,你就启动了一个进程。显然,程序是静态的,进程是动态的。进程也称为任务。在同一个时间里,同一个计算机系统中如果允许两个或两个以上的进程处于运行状态,这便是多任务。现代的操作系统几乎都是多任务操作系统,能够同时管理多个进程的运行。多任务带来的好处是明显的,比如你可以边听音乐边上网,而这些任务之间丝毫不会相互干扰。

对计算机来说,原则上一个 CPU 只能分配给一个进程,以便运行这个进程。而对于只有一个 CPU 的计算机来说,要想同时运行多个进程,就必须使用并发技术。对于并发技术最容易理解的是“时间片轮转进程调度算法”,即在操作系统的管理下,所有正在运行的进程轮流使用 CPU,每个进程允许占用 CPU 的时间非常短(比如 10 毫秒),这样用户根本感觉不出来 CPU 是在轮流为多个进程服务,就好象所有的进程都在不间断地运行一样。但实际上在任一时间点有且仅有一个进程占有 CPU。如果一台计算机有多个CPU,情况就不同了,如果进程数小于 CPU 数,则不同的进程可以分配给不同的CPU来运行,这样,多个进程就是真正同时运行的,这便是并行。但如果进程数大于 CPU 数,则仍然需要使用并发技术。

狭义上的进程是操作系统的基本执行单位。在 Windows 中,线程是基本执行单位,而进程是一个容纳线程的容器。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。同一进程中的多个线程将共享该进程中的全部系统资源,如虚拟地址空间、文件描述符和信号处理等等。但同一进程中的多个线程各自拥有有名义上私有的调用栈(call stack)、寄存器上下文(register context) 和线程本地存储(thread-local storage)。

由于同一进程内的所有线程共享全部系统资源,所以线程间通信不需要特殊的数据传送机制,不需要建立共享存储区或共享文件,从而使得不同任务之间的协调操作与运行、数据的交互、资源的分配等问题更加易于解决。

与多进程相比,多线程具有以下特点:1

>>> 阅读全文

 

,

无锁双向链表

基于锁的同步机制总是受到象优先级反转(priority inversion )和死锁的困扰,即使这样,无锁同步还未引起重视。原因是要编写正确且高效的无锁代码实在困难1。本文演示了一个无锁双向链表的实现,实用且易于理解。第一个无锁双向链表是由 H°akan Sundell 实现的,本文中的方案利用了单字 (single-word) 的比较并转换(CAS — compare-and-swap)原子原语。在附录中提供了本方案与 H°akan Sundell 实现的链表的比较结果。 2

背景: 原子操作与 CAS
原子操作是指一系列必须整体完成的操作步骤,如果任何一步操作没有完成,那么所有完成的步骤都必须回滚,这样就可以保证要么所有操作步骤都未完成,要么所有操作步骤都被完成。在单核系统里,单个的机器指令可以看成是原子操作(如果有编译器优化、乱序执行等情况除外);在多核系统中,单个的机器指令就不是原子操作,因为多核系统里是多指令流并行运行的,一个核在执行一个指令时,其他核同时执行的指令有可能操作同一块内存区域,从而出现数据竞争现象。多核系统中的原子操作通常使用内存栅障(memory barrier)来实现,即一个 CPU 核在执行原子操作时,其他 CPU 核必须停止对内存操作或者不对指定的内存进行操作,这样才能避免数据竞争问题。3

因此在 X86 平台上,Intel 提供了在指令执行期间对总线加锁的手段。CPU 芯片上有一条引线 #HLOCKpin,如果汇编程序中的某条指令带了前缀 “LOCK”,CPU 执行到这条指令时就会 #HLOCKpin 的电位拉低直到该指令结束,从而将总线锁住,这样同一总线上其它 CPU 就暂时不能通过总线访问内存了,这样保证了指令在多处理器环境中的原子性。

所谓比较并交换 (CAS) 是在多线程中可用来获得同步的原子指令。CAS 操作包含三个操作数 —— 目标值(V)、原值(A)和新值(B)。CAS 指令会先将目标值 V 与原值 A 进行比较,如果二者相同,那么就可以新值 B 赋予目标值。

在 Windows 和 .NET 平台,由于历史原因,CAS 被写做 Interlocked API。原子操作在 Intel 架构 CPU 对应的汇编指令有 XCHG、CMPXCHG、INC 等,当然还得加上 LOCK 作为前缀。

>>> 阅读全文

 

, , ,

并行程序中的同步与通讯

在任何程序中,总会有些执行阶段要等待操作系统或者其他进程完成 I/O 操作或者同步操作。这时候,程序就无法处理其它工作,因为实际上是操作系统或者其它进程占用了宝贵的 CPU 时间。这就是滥用文件读/写操作的程序会如此之慢的原因。1

这时候进程不得不等待操作系统服务例程将 CPU 从其他进程那里释放出来。这种等待也被称为进程处于阻塞状态。诸如 EDA 这类串行计算,整个算法作为单个任务被执行,如果该进程在等待任一种阻塞操作,整个算法就会完全停顿。在处于阻塞状态的这段时间里,另一个进程占用了 CPU 的所有权,所以算法根本没办法进行。

并行程序提供了一种手段,能在同一计算中调用另一个进程以提高 CPU 利用率,当一个进程被阻塞的时候(如等待 I/O 操作或者同步操作),另一个进程则继续处理共同工作的其它部分。这种情况在单 CPU 或者多 CPU 机器上皆可发生。

反之,对于串行程序(即非并行算法)来说,而无论有多少可用的 CPU ,在任何情况下都只能用到单个 CPU。除非有编译器将源程序从串行转成并行,不过能实现这一目标的实属特例。 因此如果想同时利用多个 CPU ,编码时要手工运用某种并行技术。(在源代码中采用特殊指令,如 OpenMP)。

并行程序能将工作划分为较小的部分,因此多个进程可以一起工作并互相协作以便能在更短的时间内计算出同样的结果。这时,在等待 I/O 设备完成操作的同时,并行程序能可以继续处理另一部分工作。此外,并行程序还可以同时使用系统所有可用的 CPU。

>>> 阅读全文

 

, , , , , , ,