20.3 线程的同步

来源:互联网 发布:什么是java迭代器 编辑:程序博客网 时间:2024/05/16 06:51

摘录于《Windows程序(第5版,珍藏版).CHarles.Petzold 著》P945

        大概每年一次,我公寓外繁忙的十字路口的交通信号灯都会停止工作。结果是一片混乱。尽管车子会尽量避免碰撞,但它们还是会贴得很近。

        我们把两条路的交叉叫“临界区”,朝南开的车和朝西开的车不可能同时穿过交叉路口而不相撞。根据交通流量,可以采取不同的方法来解决这个问题。对于流量小能见度高的路口,司机们可以自主退让。在流量适中的路口,可以使用停止信号。对于大流量路口,则必须使用红绿灯。红绿灯可以协调十字路口的交通(当然,前提是它们必须正常工作)。

临界区

        在单任务操作系统中,传统的程序不需要红绿灯来协调它们的行动。在它们运行时,他们拥有完全的路权。没有任何活动能干扰它们。

        即使在多任务操作系统中,大部分程序看起来也是独立运行的。但是,有时会出现问题。比如,两个程序要同时读写同一个文件。在这种情况下,操作系统提供文件共享机制记录锁定来帮助解决问题。

        但是,在一个支持多线程的操作系统中,问题变得复杂和危险。两个或多个线程共享数据变得很常见。比如,一个线程正在更新一个或多个数据,而另一个线程正在使用这些数据。有时这会引起问题,有时则不会。(值得指出的是,操作系统只能在机器指令之间从一个线程切换到另一个线程。如果只是单个整数被共享,则由于通常对该变量的改变是一个指令完成的,所以一般不会有问题。)

        假设线程间共享了多个变量或一个数据结构。通常,这些变量或数据结构的字段必须一致。操作系统可以在线程更新变量的过程中中断线程。这时,使用这些数据的线程会看到不一致的数据

        这样的结果的数据冲突,并可能导致程序崩溃。为此,我们需要使用和红绿灯等同的编程方案来协调和同步线程的数据。这就是临界区。简单地讲,一个临界区就是一段不会被中断的代码

        使用临界区共有四个函数。为了使用这些函数,你必须定义一个临界区对象,这个对象是一个类型为 CRITICAL_SECTION 的全局变量。比如,

CRITICAL_SECTION cs;
这个 CRITICAL_SECTION 数据类型是一个结构,但它的字段只被 Windows 内部使用。这个变量必须首先被程序中的一个线程通过如下方式初始化:
InitializeCriticalSection (&cs);
这创立了一个叫 cs 的临界区对象。这个函数的联机文档包括以下警告:“临界区对象不能被移动或复制。程序不可以修改它,并且必须把它当作不透明的对象。”说白了,这可以被理解为“不要改动或甚至读取它。”

        在临界区对象被初始化后,一个线程通过调用以下函数来进入临界区:

EnterCriticalSection (&cs);
这时,我们就说这个线程拥有这个临界区对象。两个线程不能同时拥有同一个临界区。所以,如果一个线程进入了临界区,那么下一个线程在调用 EnterCriticalSection 来进入同一个临界区对象时会被挂起。这个函数直到第一个线程调用以下函数离开临界区时才会返回:

LeaveCriticalSection (&cs);
在这时,由于调用 EnterCriticalSection 而被挂起的第二个线程会拥有这个临界区,而且函数调用会返回,使得线程可以继续。

        当程序不再需要临界区对象时,可以通过以下函数删除它:

DeleteCriticalSection (&cs);
这会释放为了维护临界区对象而分配的所有系统资源。

        这种临界区机制涉及“互斥”(mutual exclusion),我们会在线程同步的进一步探讨中再次遇到这个术语。因为在任何时刻,只有一个线程拥有临界区,所以一个线程可以进入临界区,更新数据结构的字段,然后离开临界区。另一个使用该结构的线程在访问这个结构的字段之前也要进入临界区,然后在访问结束后离开临界区。

        你可以定义多个临界区对象——比如 cs1 和 cs2.如果一个程序有四个线程,前两个线程共享一些数据,那它们可以使用一个临界区对象;后两个线程共享另外的数据,它们可以用第二个临界区对象。

        另外,如果要在主线程中使用临界区,你就必须非常小心。如果一个二级线程在临界区中停留过久,它会阻碍主线程很长时间通常,二级线程应该只使用临界区来把结构中的字段复制到它自己的局部变量

        临界区的一个局限是它只能在同一个进程中使用。如果你要协调两个共享某个资源的进程(比如共享内存),你就不能使用临界区,而应该使用一个不常用的类型——“互斥对象”(mutex object)。“mutex” 代表 “mutual exclusion”(互斥),这正是它的含义。如果一个程序的线程正在更新或访问共享内存或其他资源,你应该防止它被打断

0 0
原创粉丝点击