设为首页 加入收藏

TOP

14.4 线程同步
2013-10-07 01:19:48 来源: 作者: 【 】 浏览:73
Tags:14.4 线程 同步

14.4  线程同步

虽然多线程能带来好处,但是也有不少问题需要解决。例如,对于某一个数据来说,有可能一个线程访问这个数据,并且对其进行了更新,那么在另外一个线程中访问该数据时,是使用更新前的数值呢?还是更新后的数值,在程序中有必要对其进行管理,这就是同步问题。

使隶属于同一进程的各线程协调一致地工作称为线程的同步。MFC提供了多种同步对象,下面只介绍最常用的四种:临界区(CCriticalSection)、事件(CEvent)、互斥量(CMutex)和信号量(CSemaphore)。下面就这四种方式分别进行介绍。

1.使用CCriticalSection类

每一进程都可以包含多个线程。当多个线程需要访问一个独占性共享资源时,可以使用"临界区"对象。所谓的"临界区"对象就是说当一个线程得到一个资源时,该线程在此阶段对该资源就是独占的。只有当该线程释放掉该资源时,其他线程才可以得到该资源。这样就保证了不会在同一时刻出现多个线程访问共享资源。在MFC中采用CCriticalSection类对"临界区"同步进行了封装。该类控制线程同步的基本步骤如下:

(1)定义CCriticalSection类的一个全局对象,如CCriticalSection critical_section;。

(2)在访问需要保护的资源或代码之前,调用CCriticalSection类的成员函数Lock()获得临界区对象,这样该线程就唯一地获得该资源的使用权。

(3)在线程对资源的使用结束时,可以使用CCriticalSection的成员函数Unlock()来释放临界区。这时其他的线程就可以同样利用Lock()函数获取资源的使用权。

注意:在线程中使用Lock()函数获取临界区对象时,如果临界区正在被其他线程所访问,该线程会直接被挂起,直到临界区被释放。这也是保证临界区被唯一访问的基本原理。

2.使用CEvent类

除了使用"临界区"保证线程同步之外,在MFC中提供了一种事件机制来进行线程同步的控制。所谓的事件机制是指在一个线程中可以利用事件影响其他线程。在MFC中采用CEvent类对事件同步进行了封装。该类的每一个对象可以看做是一个事件。每一个CEvent对象,即事件可以有两种状态:有信号状态和无信号状态。线程监视位于其中的CEvent类对象的状态,当事件对象被其他线程释放时就有可能产生动作。

对于事件,MFC中将其分为两类:人工事件和自动事件。所谓的自动事件是指在线程结束时,系统会自动将线程中使用的事件对象设置为无信号状态。而人工事件对象则是在调用成员函数ReSetEvent()时才将其设置为无信号状态。在创建CEvent类的对象时,默认创建的是自动事件。

下面对CEvent 类的各成员函数的原型和参数说明如下:

(1)CEvent 类构造函数,函数原型如下:

  1. CEvent( BOOL bInitiallyOwn=FALSE,BOOL bManualReset=FALSE,LPCTSTR   
  2.          lpszName=NULL,  
  3.          LPSECURITY_ATTRIBUTES lpsaAttribute=NULL); 

各个参数的意义如下:

bInitiallyOwn:指定事件对象初始化状态,TRUE为有信号,FALSE为无信号。

bManualReset:指定要创建的事件是属于人工事件还是自动事件,TRUE为人工事件,FALSE为自动事件。

(2)SetEvent()函数原型如下:

  1. BOOL CEvent::SetEvent(); 

将CEvent 类对象的状态设置为有信号状态。如果该函数执行成功,则返回非零值,否则返回零。

(3)ResetEvent()函数原型如下:

  1. BOOL CEvent::ResetEvent(); 

该函数将事件的状态设置为无信号状态,并保持该状态直至SetEvent()函数被调用时为止。由于自动事件是由系统自动重置,故自动事件不需要调用该函数。如果该函数执行成功,返回非零值,否则返回零。

3.使用CMutex类

互斥对象与临界区的含义基本相同。互斥对象可以在进程间使用,而临界区对象只能在同一进程的各线程间使用。当然,互斥对象也可以用于同一进程的各个线程间。下面讨论的就是在同一个进程中的不同线程中使用互斥对象实现线程同步。

4.使用CSemaphore类

当需要一个计数器来限制可以使用某个线程的数目时,可以使用"信号量"对象。CSemaphore类的对象保存了对当前访问某一指定资源的线程的计数值。该计数值是当前还可以使用该资源的线程的数目。一个线程被释放已访问了被保护的资源时,计数值减1。一个线程使用了共享资源时,计数值增1。这个被CSemaphore 类对象所控制的资源可以同时接受访问的最大线程数在该对象的构建函数中指定。CSemaphore 类的构造函数原型及参数说明如下:

  1. CSemaphore (LONG lInitialCount=1,LONG lMaxCount=1,
    LPCTSTR pstrName=NULL, LPSECURITY_ATTRIBUTES lpsaAttributes=NULL); 

lInitialCount:信号量对象的初始计数值,即可访问线程数目的初始值。

lMaxCount:信号量对象计数值的最大值,该参数决定了同一时刻可访问由信号量保护的资源的线程最大数目。

在用CSemaphore 类的构造函数创建信号量对象时,指出允许的最大资源计数和当前可用资源计数。一般是将当前可用资源计数设置为最大资源计数,每增加一个线程对共享资源的访问,当前可用资源计数就会减1,只要当前可用资源计数是大于0的,就可以发出信号量信号。当可用资源数减小到0时,不再允许其他线程的进入。线程在处理完共享资源后,应在离开的同时通过ReleaseSemaphore()函数将当前可用资源数加1。

上面介绍了线程同步的几种方法。线程的同步是多线程编程(www.cppentry.com)中最为重要的要求之一,只有保证了线程的同步,才可以保证一些数据操作等是正确的。

【责任编辑:云霞 TEL:(010)68476606】

回书目   上一节   下一节

】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇14.5 创建线程实例 下一篇14.7 上机实践

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: