第二十章 多任务和多线程(线程同步)

来源:互联网 发布:田径女神走红网络 编辑:程序博客网 时间:2024/06/01 10:23

线程同步

大约每年一次,在我公寓窗外的交通繁忙地段的红绿灯会停止工作。结果是造成交通的混乱,虽然轿车一般能避免撞上别的轿车,但是这些车经常挤在一起。

我用术语称两条路相交的十字路口为「临界区域」。一辆向南的车和一辆向西的车不可能同时通过一个十字路口而不撞着对方。依赖于交通流量,可以采用不同的方法来解决这个问题。对于视野清楚车辆稀少的路口,可以相信司机有处理的能力。车辆增多可能会要求一个停车号志,而更加繁忙的交通则将要求有红绿灯,红绿灯有助于协调路口的交通(当然,这些灯号必须正常工作)。

临界区域

在单工操作系统中,传统的计算机程序不需要红绿灯来帮助协调它们之间的行为。它们在执行时似乎独占了整条路,而且也确实是这样,没有什么会干扰它们的工作。

即使在多任务操作系统中,大多数的程序也似乎各自独立地在执行,但是可能会发生一些问题。例如,两个程序可能会需要同时从同一个文件中读或者对同一文件进行写。在这种情况下,操作系统提供了一种共享文件和记录上锁的技术来帮助解决这个问题。

然而,在支持多线程的操作系统中,情况会变得混乱而且存在潜在的危险。两个或多个线程共享某些数据的情况并不罕见。例如,一个线程可以更新一个或者多个变量,而另一个线程可以使用这些变量。有时这会引发一个问题,有时又不会(记住操作系统将控制权从一个线程切换到另一个线程的操作,只能在机器码指令之间发生。如果只是一个整数被线程共享,那么对这个变量的改变通常发生在单个指令中,因此潜在的问题被最小化了)。

然而,假设线程共享几个变量或者数据结构。通常,这么多个变量或者结构的字段在它们之间必须是一致的。操作系统可以在更新这些变量的程序中间中断一个线程,那么使用这些变量的线程得到的将是不一致的数据。

结果是冲突发生了,并且通常不难想象这样的错误将对程序造成怎样的破坏。我们所需要的是类似于红绿灯的程序写作技术,以帮助我们对线程交通进行协调和同步,这就是临界区域。大体上,一个临界区域就是一块不可中断的程序代码。

有四个函数用于临界区域。要使用这些函数,您必须定义一个临界区域对象,这是一个型态为CRITICAL_SECTION的整体变量。例如:

CRITICAL_SECTION cs ;

这个CRITICAL_SECTION数据型态是一个结构,但是其中的字段只能由Windows内部使用。这个临界区域对象必须先被程序中的某个线程初始化,通过呼叫:

InitializeCriticalSection (&cs) ;

这样就建立了一个名为cs的临界区域对象。该函数的在线辅助说明包含下面的警告:「临界区域对象不能被移动或者复制,程序也不能修改该对象,但必须在逻辑上把它视为不透明的。」这句话,可以被解释为:「不要干扰它,甚至不要看它。」

当临界区域对象被初始化之后,线程可以通过下面的呼叫进入临界区域:

EnterCriticalSection (&cs) ;

在这时,线程被认为「拥有」临界区域对象。两个线程不可以同时拥有同一个临界区域对象,因此,如果一个线程进入了临界区域,那么下一个使用同一临界区域对象呼叫EnterCriticalSection的线程将在函数呼叫中被暂停。只有当第一个线程通过下面的呼叫离开临界区域时,函数才会传回控制权:

LeaveCriticalSection (&cs) ;

这时,在EnterCriticalSection呼叫中被停住的那个线程将拥有临界区域,其函数呼叫也将传回,允许线程继续执行。

当临界区域不再被程序所需要时,可以通过呼叫

DeleteCriticalSection (&cs) ;

将其删除,该函数释放所有被配置来维护此临界区域对象的系统资源。

这种临界区域技术涉及「互斥」(此术语在我们继续讨论线程同步时将再次出现)。在任何时刻,只有一个线程能拥有一个临界区域。因此,一个线程可以进入一个临界区域,设定一个结构的字段,然后退出临界区域。另一个使用该结构的线程在存取结构中的字段之前也要先进入该临界区域,然后再退出临界区域。

注意,您可以定义多个临界区域对象,比如cs1和cs2。例如,如果一个程序有四个线程,而前两个线程共享一些数据,那么它们可以使用一个临界区域对象,而另外两个线程共享一些其它的数据,那么它们可以使用另一个临界区域对象。

您在主线程中使用临界区域时应该小心。如果从属线程在它自己的临界区域中花费了一段很长的时间,那么它可能会将主线程的执行阻碍很长一段时间。从属执行绪可能只是使用临界区域复制该结构的字段到自己的区域变量中。

临界区域的一个限制是它们只能用于在同一程序内的线程之间的协调。但是在某些情况下,您需要协调两个不同程序对同一资源的共享(如共享内存等)。在此其况下不能使用临界区域,但是可以使用一种被称为「互斥对象(mutex object)」的技术。「mutex」是个合成字,代表「mutual exclusion(互斥)」,它在这里精确地表达了我们的目的。我们想防止一个程序的线程在更新数据或者使用共享内存与其它资源时被中断。

 

==================================================

 

原创粉丝点击