您的位置:首页 > 其它

秒杀多线程-经典线程同步之互斥量Mutex

2016-10-31 20:22 337 查看
本文转载自 秒杀多线程第七篇 经典线程同步 互斥量Mutex

前面介绍了关键段CS、事件Event在经典线程同步问题中的使用。本篇介绍用互斥量Mutex来解决这个问题。互斥量也是一个内核对象,它用来确保一个线程独占一个资源的访问。互斥量与关键段的行为非常相似,并且互斥量可以用于不同进程中的线程互斥访问资源。使用互斥量Mutex主要将用到四个函数。下面是这些函数的原型和使用说明。

HANDLE WINAPI CreateMutex(

  _In_opt_ LPSECURITY_ATTRIBUTES lpMutexAttributes,

  _In_     BOOL                  bInitialOwner,

  _In_opt_ LPCTSTR               lpName

);

函数功能:创建并打开一个命名或未命名的互斥对象。

参数说明:

第一个参数表示安全控制,一般直接传入NULL。

第二个参数用来确定互斥量的初始拥有者。如果此值为TRUE并且调用程序创建了互斥锁,则调用线程将获取互斥锁对象的初始所有权。 否则,调用线程不会获取互斥体的所有权。

第三个参数表示互斥量的名称。

HANDLE WINAPI OpenMutex(

  _In_ DWORD   dwDesiredAccess,

  _In_ BOOL    bInheritHandle,

  _In_ LPCTSTR lpName

);

函数功能:打开现有的命名的互斥对象。

参数说明:

第一个参数表示访问权限,一般直接传入MUTEX_ALL_ACCESS。

第二个参数表示互斥量句柄继承性,一般直接传入TRUE。

第三个参数表示互斥量的名称。

BOOL WINAPI ReleaseMutex(

  _In_ HANDLE hMutex

);

函数功能:释放指定互斥体对象的所有权。

参数说明:互斥对象的句柄。 CreateMutex或OpenMutex函数返回此句柄。

由于事件是内核对象,因此使用CloseHandle()就可以完成清理与销毁了。

接下来我们就在经典多线程问题用互斥量来保证主线程与子线程之间的同步,由于互斥量的使用函数类似于事件Event,所以可以仿照上一篇的实现来写出代码。

#include <stdio.h>
#include <process.h>
#include <windows.h>

long g_nNum;
unsigned int __stdcall Fun(void *pPM);
const int THREAD_NUM = 10;
//互斥量与关键段
HANDLE  g_hThreadParameter;
CRITICAL_SECTION g_csThreadCode;

int main()
{
printf("     经典线程同步 互斥量Mutex\n");
printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");
//初始化互斥量与关键段第二个参数为TRUE表示互斥量为创建线程所有
g_hThreadParameter = CreateMutex(NULL, FALSE, NULL);
InitializeCriticalSection(&g_csThreadCode);
HANDLE  handle[THREAD_NUM];
g_nNum = 0;
int i = 0;
while (i < THREAD_NUM)
{
handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL);
//等待互斥量被触发
WaitForSingleObject(g_hThreadParameter, INFINITE);
i++;
}
WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
//销毁互斥量和关键段
CloseHandle(g_hThreadParameter);
DeleteCriticalSection(&g_csThreadCode);
for (i = 0; i < THREAD_NUM; i++) CloseHandle(handle[i]);
return 0;
}

unsigned int __stdcall Fun(void *pPM)
{
int nThreadNum = *(int *)pPM;
//触发互斥量
ReleaseMutex(g_hThreadParameter);
Sleep(50);
EnterCriticalSection(&g_csThreadCode);
g_nNum++;
printf("线程编号为%d  全局资源值为%d\n", nThreadNum, g_nNum);
LeaveCriticalSection(&g_csThreadCode);
return 0;
}



可以看出,与关键段类似,互斥量也是不能解决线程间的同步问题。联想到关键段会记录线程ID即有“线程拥有权”的,而互斥量也记录线程ID,莫非它也有“线程拥有权”这一说法。答案确实如此,互斥量也是有“线程拥有权”概念的。“线程拥有权”在关键段中有详细的说明,这里就不再赘述了。另外由于互斥量常用于多进程之间的线程互斥,所以它比关键段还多一个很有用的特性——“遗弃”情况的处理。比如有一个占用互斥量的线程在调用ReleaseMutex()触发互斥量前就意外终止了(相当于该互斥量被“遗弃”了),那么所有等待这个互斥量的线程是否会由于该互斥量无法被触发而陷入一个无穷的等待过程中了?这显然不合理。因为占用某个互斥量的线程既然终止了那足以证明它不再使用被该互斥量保护的资源,所以这些资源完全并且应当被其它线程来使用。因此在这种“遗弃”情况下,系统自动把该互斥量内部的线程ID设置为0,并将它的递归计数器复置为0,表示这个互斥量被触发了。然后系统将“公平地”选定一个等待线程来完成调度(被选中的线程的WaitForSingleObject()会返回WAIT_ABANDONED_0)。

下面写两个程序来验证下。第一个程序创建互斥量并等待用户输入后就触发互斥量。第二个程序先打开互斥量,成功后就等待并根据等待结果作相应的输出。
#include <stdio.h>
#include <conio.h>
#include <windows.h>
const char MUTEX_NAME[] = "Mutex_MoreWindows";

int main()
{
HANDLE hMutex = CreateMutex(NULL, TRUE, MUTEX_NAME);
printf("互斥量已经创建,现在按任意键触发互斥量\n");
getchar();
//exit(0);
ReleaseMutex(hMutex);
printf("互斥量已经触发\n");
CloseHandle(hMutex);
system("pause");
return 0;
}
#include <stdio.h>
#include <windows.h>
const char MUTEX_NAME[] = "Mutex_MoreWindows";

int main()
{
HANDLE hMutex = OpenMutex(MUTEX_ALL_ACCESS, TRUE, MUTEX_NAME);
if (hMutex == NULL)
{
printf("打开互斥量失败\n");
return 0;
}
printf("等待中....\n");
DWORD dwResult = WaitForSingleObject(hMutex, 20 * 1000);
switch (dwResult)
{
case WAIT_ABANDONED:
printf("拥有互斥量的进程意外终止\n");
break;
case WAIT_OBJECT_0:
printf("已经收到信号\n");
break;
case WAIT_TIMEOUT:
printf("信号未在规定的时间内送到\n");
break;
}
CloseHandle(hMutex);
return 0;
}
先运行第一个程序再运行第二个程序。



按下一个键以后。



现在将程序一中//exit(0);前面的注释符号去掉,这样程序一在触发互斥量之前就会因为执行exit(0);语句而且退出,程序二会收到WAIT_ABANDONED消息并输出“拥有互斥量的进程意外终止”。



有对这个问题的处理,在多进程中的线程同步也可以放心的使用互斥量。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: