当两个或更多线程需要同时访问一个共享资源时,为了保证程序的正常运行,需要保证同一个资源在同一时刻只能有一个线程去访问它。Mutex 是同步基元,它只向一个线程授予对共享资源的独占访问权。如果一个线程获取了互斥体,则要获取该互斥体的第二个线程将被挂起,直到第一个线程释放该互斥体。
互斥量与关键段(线程同步方式)的行为完全相同,当互斥量是内核对象,而关键段是用户模式下的的同步对象。互斥量对象包含: 一个线程 ID ,使用计数和递归计数 。线程 ID 表示当前占用该互斥量的线程 ID ,递归计数表示该线程占用互斥量的次数,使用计数表示使用互斥量对象的不同线程的个数。
互斥量对象有许多用途,它是使用最为频繁的内核对象之一,一般用来对多个进程访问同一块内存进行同步。
HANDLE WINAPI CreateMutex(
_In_opt_ LPSECURITY_ATTRIBUTES lpMutexAttributes,
_In_ BOOL bInitialOwner,
_In_opt_ LPCTSTR lpName
);
参数说明
lpMutexAttributes:互斥量安全访问属性,一般置为 NULL ;
bInitialOwner:控制互斥量的初始状态,一般设置为 false ,是互斥量的线程 ID 和递归计数都设为 0 ,表示互斥量不被任何进程占用,处于触发状态,其他进程可以进行调用等待函数,获得该互斥量对象,获得对同步资源的占用。如果初始值设为 true ,互斥量的线程 ID 设置为当前线程,递归计数设为 1 ,表示当前线程占用互斥量对象,拥有对同步资源的独占,互斥量处于未触发状态。
lpName:用于创建有名的内核对象,即用来创建跨进程边界的内核对象。
CreateMutex 用于创建名为 lpName 的互斥量内核对象,并返回指向该内核对象的句柄。如果该内核对象已经存在,那么 CreateMutex 会返回该内核对象的句柄,并通过系统返回错误 ERROR_ALREADY_EXISTS ,通过 GetLastError ()获得。
HANDLE WINAPI OpenMutex(
_In_ DWORD dwDesiredAccess,
_In_ BOOL bInheritHandle,
_In_ LPCTSTR lpName
);
参数说明
dwDesiredAccess:对互斥量对象访问权限的设置, MUTEX_ALL_ACCESS 请求对互斥体的完全访问, MUTEX_MODIFY_STATE 允许使用 ReleaseMutex 函数, SYNCHRONIZE 允许互斥体对象同步使用;
bInheritHandle:是否希望子进程继承互斥量对象的句柄,一般设置为false ;
lpName:要打开的互斥量对象的名称;
OpenMutex 用于打开已经存在的互斥量对象,若打开成功,则返回指向该内核对象的句柄,否则返回 NULL 。可以使用 CreateMutex 来实现打开功能。
DWORD WINAPI WaitForSingleObject(
_In_ HANDLE hHandle,
_In_ DWORD dwMilliseconds
);
参数说明
hHandle :指向内核对象的句柄;
dwMilliseconds:线程最大等待多长时间,直到该对象被触发。经常使用INFINITE ,表示阻塞等待。
WaitForSingleObject被称呼为等待函数,是等待内核对象被触发通用的等待函数,被用在所有的内核对象触发等待中。等待函数在等待互斥量内核对象时,会进行互斥量的线程ID 是否为 0 ,如果为非 0 ,表示互斥量处于未触发状态,等待函数会被阻塞。当另外一个线程将互斥量释放,使其线程 ID 为 0 时,系统会唤醒阻塞的等待函数,把互斥量的线程 ID 设置为它的线程 ID ,使其成为可调度状态 。
还有一个WaitForMultipleObject函数,用于等待多个内核对象被触发。
BOOL WINAPI ReleaseMutex(
_In_ HANDLE hMutex
);
参数说明
hMutex:互斥量内核对象的句柄;当一个线程访问完通过互斥量对象获得的独占资源后,应该调用ReleaseMutex,使互斥量恢复为未触发状态。即设置互斥量对象的线程ID 和递归计数为 0 ,当递归计数大于 1 时,还有进行对应多次的 ReleaseMutex 。
DWORD WINAPI my_thread1(LPVOID m_pParameter); //用户线程1
DWORD WINAPI my_thread2(LPVOID m_pParameter); //用户线程2
UINT count = 0; //全局的计数函数
HANDLE m_hMutex; //定义一个互斥量句柄
int _tmain(int argc, _TCHAR* argv[])
{
system("color f0");
int count_size(50);
m_hMutex = ::CreateMutex(NULL, false, NULL); //初始化互斥量
HANDLE m_h1 = CreateThread(NULL, NULL, my_thread1, &count_size, NULL, NULL); //用户线程1
HANDLE m_h2 = CreateThread(NULL, NULL, my_thread2, &count_size, NULL, NULL); //用户线程2
::WaitForSingleObject(m_h1, INFINITE); //等待子线程结束
::WaitForSingleObject(m_h2, INFINITE); //等待子线程结束
::CloseHandle(m_hMutex); //关闭句柄
::CloseHandle(m_h1);
::CloseHandle(m_h2);
system("pause");
return 0;
}
//用户线程1
DWORD WINAPI my_thread1(LPVOID m_pParameter)
{
UINT* my_count = (UINT*)m_pParameter;
cout << "thread1" << endl;
for (int i = 0; i < *my_count; ++i)
{
::WaitForSingleObject(m_hMutex, INFINITE); //等待互斥量
count = i;
cout << count << "\t";
::ReleaseMutex(&m_hMutex); //释放互斥量
}
return 0;
}
//用户线程2
DWORD WINAPI my_thread2(LPVOID m_pParameter)
{
UINT* my_count = (UINT*)m_pParameter;
cout << "thread2" << endl;
for (int i = 0; i < *my_count; ++i)
{
::WaitForSingleObject(m_hMutex, INFINITE); //等待互斥量
count = i;
cout << count << "\t";
::ReleaseMutex(&m_hMutex); //释放互斥量
}
return 0;
}