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

孙鑫VC学习笔记:第十五讲 (三) 增加互斥条件实现线程同步

2013年12月08日 ⁄ 综合 ⁄ 共 3910字 ⁄ 字号 评论关闭
增加互斥条件,实现线程同步。

一、创建互斥对象 :
 HANDLE CreateMutex(
 LPSECURITY_ATTRIBUTES lpMutexAttributes,// 安全性
 BOOL bInitialOwner,  // flag for initial ownership,
 LPCTSTR lpName     // pointer to mutex-object name
 );
打开一个命名的或者没有名字的互斥对象:
参数1:指向SECURITY_ATTRIBUTES结构体的指针。可以传递NULL,让其使用默认的安全性。
参数2:指示互斥对象的初始拥有者。
 如果该值是真,调用者创建互斥对象,调用的线程获得互斥对象的所有权。
 否则,调用线程捕获的互斥对象的所有权。(就是说,如果该参数为真,则调用
 该函数的线程拥有互斥对象的所有权。否则,不拥有所有权)
参数3:互斥对象名称。传递NULL创建的就是没有名字的互斥对象,即匿名的互斥对象。
Return Values  
If the function succeeds, the return value is a handle to the mutex object.
创建成功之后 ,返回一个互斥对象句柄。
If the named mutex object existed before the function call, the function returns a handle to the existing object and GetLastError returns ERROR_ALREADY_EXISTS.
如果一个命名的互斥对象在本函数调用之前已经存在,则返回已经存在的对象句柄。
然后可以调用GetLastError检查其返回值是否为ERROR_ALREADY_EXISTS,
TRUE则表示命名互斥对象已经存在,否则表示互斥对象是新创建的。
Otherwise, the caller created the mutex. 否则创建一个互斥对象。
If the function fails, the return value is NULL. To get extended error information, call GetLastError.

注:
 创建匿名互斥对象时 HANDLE hMutex = CreateMutex(NULL, FALSE, NULL);
 当前没有线程拥有互斥对象,操作系统会将互斥对象设置为已通知状态(有信号状态)
 
另:关于互斥对象

*互斥对象(Mutex)属于内核对象,它能确保线程拥有对单个资源的互斥访问权。
*互斥对象包含一个使用数量,一个线程ID(哪个线程拥有互斥对象,就设置为哪个线程的线程ID)
 和一个计数器。
*ID用于标识系统中的哪个线程当前拥有互斥对象,计数器用于指明该线程拥有互斥对象的次数。
--------------------------------------------------------------------------------
二、在线程中请求互斥对象
 DWORD WaitForSingleObject(HANDLE hHandle,DWORD dwMilliseconds);
 出现下面情况之一时返回:
 1、指定的对象处于有信号状态;
 2、超出了超时(time-out)的时间间隔。

 DWORD WaitForSingleObject(
 HANDLE hHandle,     // handle to object to wait for,
DWORD dwMilliseconds   // time-out interval in milliseconds
 );
 参数1:对象的句柄,这里传递的是互斥对象的句柄。
  一旦互斥对象变成有信号状态,该函数返回。
  如果互斥对象始终没有处于有信号状态(非信号状态),
  函数将一直处于等待,从而导致线程暂停运行。
 参数2:指定超时的时间间隔,以毫秒为单位。
  如果时间间隔流逝了,函数就返回,即使等待的互斥对象处于非信号状态;
  如果将该参数设置为0,该函数测试对象的状态后立即返回;
  如果将该参数设置为INFINITE,函数的超时值永远不会发生,
  也就是说函数将永远等待,直到所等待的对象处于有信号状态。
 
 Return Values
 If the function succeeds, the return value indicates the event that caused the function to return. This value can be one of the following.
 
 WAIT_ABANDONED
  当拥有互斥对象的线程异常终止或者终止之前没有调用ReleaseMutex释放对象,则返回此值
  The specified object is a mutex object that was not released by the thread that owned the mutex object before the owning thread terminated. Ownership of the mutex object is granted to the calling thread, and the mutex is set to nonsignaled.
 WAIT_OBJECT_0
  The state of the specified object is signaled.
 WAIT_TIMEOUT
  The time-out interval elapsed, and the object's state is nonsignaled.
  
 注意:
  可以在我们需要保护的代码前面加上WaitForSingleObject(),
  当我们请求互斥对象的时候操作系统会判断请求互斥对象的线程
  和拥有互斥对象的线程的ID是否相等,如果相等,即使互斥对象处于未通知状态
  (非信号状态),仍然能够获得互斥对象的所有权。
  操作系统通过互斥对象的计数器记录请求了多少次互斥对象。
 另:可通过该函数的返回值,得知如何获得的互斥对象所有权。
 
--------------------------------------------------------------------------------
三、释放互斥对象:
在所要保护的代码操作完成之后,要用ReleaseMutex方法释放互斥对象。
 BOOL ReleaseMutex(
 HANDLE hMutex   // handle to mutex object
 );
  //本函数如果成功返回非0值,失败返回0。
 调用本函数会将互斥对象的ID设置为0,并使互斥对象处于
 已通知状态(有信号状态),同时将互斥对象的计数器减一。
注意:
 本函数只能被拥有互斥对象所有权的线程调用,其他线程无法释放互斥对象。
 因为互斥对象会保存所有者的线程ID,在调用ReleaseMutex时会先判断一下这个线程与
 互斥对象保存的ID是否一致,如果不是一致则不能成功释放。
 释放互斥对象的原则:谁拥有,谁释放。
---------------------------------------------------------------------------------
四、调用的形式
 //在主线程中
 ...
 HANDLE hMutex = CreateMutex(NULL, FALSE, NULL);
 ...
 //其他线程中
 ...
 WaitForSingleObject(hMutex, INFINITE);
 //受保护的代码
 ...
 ReleaseMutex(hMutex);

--------------------------------------------------------------------------------

注意:1.
 互斥对象包含一个计数器,用来记录互斥对象请求的次数,
 所以在同一线程中请求了多少次就要释放多少次;
 如 hMutex=CreateMutex(NULL,TRUE,NULL);
  //当第二个参数设置为TRUE时,互斥对象计数器设为1
 WaitForSingleObject(hMutex,INFINITE);
  //因为请求的互斥对象线程ID与拥有互斥对象线程ID相同,可以再次请求成功,计数器加1
 ReleaseMutex(hMutex);  //第一次释放,计数器减1,但仍有信号
 ReleaseMutex(hMutex);  //再一次释放,计数器为零
 
 2.如果操作系统发现线程已经正常终止,会自动把线程申请的互斥对象ID设为0,
   同时也把计数器清零,其他对象可以申请互斥对象。
 
 3.可以根据WaitForSingleObject的返回值判断该线程是如何得到互斥对象拥有权的
  如果返回值是WAIT_OBJECT_0,表示由于互斥对象处于有信号状态才获得所有权的
  如果返回值是WAIT_ABANDONED,则表示先前拥有互斥对象的线程异常终止
  或者终止之前没有调用 ReleaseMutex释放对象,此时就要警惕了访问资源有破坏资源的危险
--------------------------------------------------------------------------------
如何使应用程序只能有一个实例在运行
通过命名互斥来保证应用程序只能同时运行一个实例。
方法:
 首先CreateMutex创建一个命名的互斥对象,
 然后调用GetLastError,如果返回ERROR_ALREADY_EXISTS则表明互斥对象已经创建过了,
      也就是可以判断已经有一个实例在运行了,否则是调用者新创建的互斥对象。 

 

抱歉!评论已关闭.