互斥量的用途和临界区很像。它与临界区的差别在于可以跨线程使用,可以用来同步进行多个线程间的数据访问,但是是以牺牲速度为代价的。只有临界区是非核心对象,那么互斥量就是一个核心对象了。核心对象的特点是有所谓的引用计数。锁住一个未被拥有的互斥量,比锁住一个未被拥有的临界区需要花费几乎100倍的时间(数据引用自《Visual C++ 6.0编程学习捷径》)。
VC windows api 多线程---互斥量

  与其他几种内核对象不同,互斥对象在操作系统中拥有特殊代码,并由操作系统来管理,操作系统甚至还允许其进行一些其他内核对象所不能进行的非常规操作。为便于理解,可参照图6给出的互斥内核对象的工作模型:

图6 使用互斥内核对象对共享资源的保护:

CPP-基础:互斥量-LMLPHP

  图(a)中的箭头为要访问资源(矩形框)的线程,但只有第二个线程拥有互斥对象(黑点)并得以进入到共享资源,而其他线程则会被排斥在外(如图(b)所示)。当此线程处理完共享资源并准备离开此区域时将把其所拥有的互斥对象交出(如图(c)所示),其他任何一个试图访问此资源的线程都有机会得到此互斥对象。

Win32 API有一套互斥量的支持函数:

创建互斥量:

HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCTSTR lpName);
* @param lpMutexAttributes 指定安全属性,NULL表示使用默认的属性.
* @param bInitialOwner 指出创建互斥量的线程是否要成为该互斥量的最初拥有.
*   TRUE表示拥有,因此互斥量将处于无开放信号状态.
*   FALSE表示互斥量不被任何现成拥有,因此处于有开放信号状态.
* @param lpName为NULL或标识该互斥量的一个字符串的地址,任何进程或线程都可以根据此名称使用该互斥量.
*   当应用程序调用CreateMutex()函数时,系统分配一个互斥量的核心对象,把它的名字设为lpName所致的字符串.
*   该名字在进程间共享互斥量。CreateMutex()返回一个标识新互斥量对象的进程相关的句柄.

打开互斥量

HANDLE OpenMutex(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCTSTR lpName);
* @param dwDesiredAccess 可设置为MUTEX_ALL_ACCESS或SYNCHRONIZE.
* @param bInheritHandle 指出该进程创建的子进程能否继承该互斥量.
* @param lpName 指定互斥量的名字.

释放互斥量.

BOOL ReleaseMutex(HANDLE hMutex);
* @param hMutex 要释放的互斥量的句柄.

  其唯一的参数hMutex为待释放的互斥对象句柄。至于WaitForSingleObject()和WaitForMultipleObjects()等待函数在互斥对象保持线程同步中所起的作用与在其他内核对象中的作用是基本一致的,也是等待互斥内核对象的通知。但是这里需要特别指出的是:在互斥对象通知引起调用等待函数返回时,等待函数的返回值不再是通常的WAIT_OBJECT_0(对于WaitForSingleObject()函数)或是在WAIT_OBJECT_0到WAIT_OBJECT_0+nCount-1之间的一个值(对于WaitForMultipleObjects()函数),而是将返回一个WAIT_ABANDONED_0(对于WaitForSingleObject()函数)或是在WAIT_ABANDONED_0到WAIT_ABANDONED_0+nCount-1之间的一个值(对于WaitForMultipleObjects()函数)。以此来表明线程正在等待的互斥对象由另外一个线程所拥有,而此线程却在使用完共享资源前就已经终止。除此之外,使用互斥对象的方法在等待线程的可调度性上同使用其他几种内核对象的方法也有所不同,其他内核对象在没有得到通知时,受调用等待函数的作用,线程将会挂起,同时失去可调度性,而使用互斥的方法却可以在等待的同时仍具有可调度性,这也正是互斥对象所能完成的非常规操作之一。

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

例1:

 1 #include <stdio.h>
2 #include <windows.h>
3 #include <string.h>
4 #include <stdlib.h>
5 #define threadnum 10
6 typedef struct THREADDATA
7 {
8 int id;
9 char name[10];
10 int sleep;
11 }THREADDATA;
12
13 HANDLE handleMutex;
14 char * str;
15 DWORD WINAPI ThreadProc( LPVOID lpParam )
16 {
17 THREADDATA *data=(THREADDATA *)lpParam;
18 //WaitForSingleObject(handleMutex,INFINITE);
19 for(int i=0;i<10;i++)
20 {
21 WaitForSingleObject(handleMutex,INFINITE);
22 printf("thread%d:%d\n",data->id,i);
23 ReleaseMutex(handleMutex);
24 Sleep(data->sleep);
25 }
26 // ReleaseMutex(handleMutex);
27 return 0;
28 }
29 int main(int argc, char* argv[])
30 {
31 str=(char*)malloc(30);
32 THREADDATA pData[threadnum];
33 DWORD dwThreadId[threadnum];
34 HANDLE hThread[threadnum];
35 handleMutex= CreateMutex(NULL,false,NULL);
36 for(int i=0;i<threadnum;i++)
37 {
38 pData[i].id=i;
39 sprintf(pData[i].name,"yuguoqing");
40 pData[i].sleep=i*10;
41 hThread[i] = CreateThread(NULL,0,ThreadProc, pData+i, 0, dwThreadId+i);
42 }
43 WaitForMultipleObjects(threadnum, hThread, TRUE, INFINITE);
44 return 0;
45 }

例2:

  在编写程序时,互斥对象多用在对那些为多个线程所访问的内存块的保护上,可以确保任何线程在处理此内存块时都对其拥有可靠的独占访问权。下面给出的示例代码即通过互斥内核对象hMutex对共享内存快g_cArray[]进行线程的独占访问保护。下面给出实现代码清单:

 // 互斥对象
HANDLE hMutex = NULL;
char g_cArray[];
UINT ThreadProc18(LPVOID pParam)
{
 // 等待互斥对象通知
 WaitForSingleObject(hMutex, INFINITE);
 // 对共享资源进行写入操作
 for (int i = ; i < ; i++)
 {
  g_cArray[i] = 'a';
  Sleep();
 }
 // 释放互斥对象
 ReleaseMutex(hMutex);
 return ;
}
UINT ThreadProc19(LPVOID pParam)
{
 // 等待互斥对象通知
 WaitForSingleObject(hMutex, INFINITE);
 // 对共享资源进行写入操作
 for (int i = ; i < ; i++)
 {
  g_cArray[ - i - ] = 'b';
  Sleep();
 }
 // 释放互斥对象
 ReleaseMutex(hMutex);
 return ;
}
……
void CSample08View::OnMutex()
{
 // 创建互斥对象
 hMutex = CreateMutex(NULL, FALSE, NULL);
 // 启动线程
 AfxBeginThread(ThreadProc18, NULL);
 AfxBeginThread(ThreadProc19, NULL);
 // 等待计算完毕
 Sleep();
 // 报告计算结果
 CString sResult = CString(g_cArray);
 AfxMessageBox(sResult);
}

MFC中通过CMutex类  

  互斥对象在MFC中通过CMutex类进行表述。使用CMutex类的方法非常简单,在构造CMutex类对象的同时可以指明待查询的互斥对象的名字,在构造函数返回后即可访问此互斥变量。CMutex类也是只含有构造函数这唯一的成员函数,当完成对互斥对象保护资源的访问后,可通过调用从父类CSyncObject继承的UnLock()函数完成对互斥对象的释放。CMutex类构造函数原型为:

 CMutex( BOOL bInitiallyOwn = FALSE, LPCTSTR lpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL );

  该类的适用范围和实现原理与API方式创建的互斥内核对象是完全类似的,但要简洁的多,下面给出就是对前面的示例代码经CMutex类改写后的程序实现清单:

 // MFC互斥类对象
CMutex g_clsMutex(FALSE, NULL);
UINT ThreadProc27(LPVOID pParam)
{
 // 等待互斥对象通知
 g_clsMutex.Lock();
 // 对共享资源进行写入操作
 for (int i = ; i < ; i++)
 {
  g_cArray[i] = 'a';
  Sleep();
 }
 // 释放互斥对象
 g_clsMutex.Unlock();
 return ;
}
UINT ThreadProc28(LPVOID pParam)
{
 // 等待互斥对象通知
 g_clsMutex.Lock();
 // 对共享资源进行写入操作
 for (int i = ; i < ; i++)
 {
  g_cArray[ - i - ] = 'b';
  Sleep();
 }
 // 释放互斥对象
 g_clsMutex.Unlock();
 return ;
}
……
void CSample08View::OnMutexMfc()
{
 // 启动线程
 AfxBeginThread(ThreadProc27, NULL);
 AfxBeginThread(ThreadProc28, NULL);
 // 等待计算完毕
 Sleep();
 // 报告计算结果
 CString sResult = CString(g_cArray);
 AfxMessageBox(sResult);
}
05-14 11:10