秒杀多线程第七篇经典线程同步互斥量mutex(代码片段)

heishanglaoyao heishanglaoyao     2023-02-02     381

关键词:

阅读本篇之前推荐阅读以下姊妹篇:

秒杀多线程第四篇一个经典的多线程同步问题

秒杀多线程第五篇经典线程同步关键段CS

秒杀多线程第六篇经典线程同步事件Event

 

前面介绍了关键段CS事件Event经典线程同步问题中的使用。本篇介绍用互斥量Mutex来解决这个问题。

互斥量也是一个内核对象,它用来确保一个线程独占一个资源的访问。互斥量与关键段的行为非常相似,并且互斥量可以用于不同进程中的线程互斥访问资源。使用互斥量Mutex主要将用到四个函数。下面是这些函数的原型和使用说明。

第一个 CreateMutex

函数功能:创建互斥量(注意与事件Event的创建函数对比)

函数原型:

HANDLECreateMutex(

  LPSECURITY_ATTRIBUTESlpMutexAttributes,

  BOOLbInitialOwner,     

  LPCTSTRlpName

);

函数说明:

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

第二个参数用来确定互斥量的初始拥有者。如果传入TRUE表示互斥量对象内部会记录创建它的线程的线程ID号并将递归计数设置为1,由于该线程ID非零,所以互斥量处于未触发状态。如果传入FALSE,那么互斥量对象内部的线程ID号将设置为NULL,递归计数设置为0,这意味互斥量不为任何线程占用,处于触发状态。

第三个参数用来设置互斥量的名称,在多个进程中的线程就是通过名称来确保它们访问的是同一个互斥量。

函数访问值:

成功返回一个表示互斥量的句柄,失败返回NULL

 

第二个打开互斥量

函数原型:

HANDLEOpenMutex(

 DWORDdwDesiredAccess,

 BOOLbInheritHandle,

 LPCTSTRlpName     //名称

);

函数说明:

第一个参数表示访问权限,对互斥量一般传入MUTEX_ALL_ACCESS。详细解释可以查看MSDN文档。

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

第三个参数表示名称。某一个进程中的线程创建互斥量后,其它进程中的线程就可以通过这个函数来找到这个互斥量。

函数访问值:

成功返回一个表示互斥量的句柄,失败返回NULL

 

第三个触发互斥量

函数原型:

BOOLReleaseMutex (HANDLEhMutex)

函数说明:

访问互斥资源前应该要调用等待函数,结束访问时就要调用ReleaseMutex()来表示自己已经结束访问,其它线程可以开始访问了。

 

最后一个清理互斥量

由于互斥量是内核对象,因此使用CloseHandle()就可以(这一点所有内核对象都一样)。

 

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

//经典线程同步问题 互斥量Mutex
#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
");
	printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --

");
	
	//初始化互斥量与关键段 第二个参数为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);//some work should to do

	EnterCriticalSection(&g_csThreadCode);
	g_nNum++;
	Sleep(0);//some work should to do
	printf("线程编号为%d  全局资源值为%d
", 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("互斥量已经创建,现在按任意键触发互斥量
");
	getch();
	//exit(0);
	ReleaseMutex(hMutex);
	printf("互斥量已经触发
");
	CloseHandle(hMutex);
	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("打开互斥量失败
");
		return 0;
	
	printf("等待中....
");
	DWORD dwResult = WaitForSingleObject(hMutex, 20 * 1000); //等待互斥量被触发
	switch (dwResult)
	
	case WAIT_ABANDONED:
		printf("拥有互斥量的进程意外终止
");
		break;

	case WAIT_OBJECT_0:
		printf("已经收到信号
");
		break;

	case WAIT_TIMEOUT:
		printf("信号未在规定的时间内送到
");
		break;
	
	CloseHandle(hMutex);
	return 0;

运用这二个程序时要先启动程序一再启动程序二。下面展示部分输出结果:

结果一.二个进程顺利执行完毕:

技术分享图片

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

技术分享图片

有这个对“遗弃”问题的处理,在多进程中的线程同步也可以放心的使用互斥量。

 

最后总结下互斥量Mutex

1.互斥量是内核对象,它与关键段都有“线程所有权”所以不能用于线程的同步。

2.互斥量能够用于多个进程之间线程互斥问题,并且能完美的解决某进程意外终止所造成的“遗弃”问题。

 

下一篇《秒杀多线程第八篇 经典线程同步 信号量Semaphore》将介绍使用信号量Semaphore来解决这个经典线程同步问题。

 

 

转载请标明出处,原文地址:http://blog.csdn.net/morewindows/article/details/7470936

如果觉得本文对您有帮助,请点击支持一下,您的支持是我写作最大的动力,谢谢。


 

再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!希望你也加入到我们人工智能的队伍中来!http://www.captainbed.net


经典线程同步信号量semaphore

阅读本篇之前推荐阅读以下姊妹篇:《秒杀多线程第四篇一个经典的多线程同步问题》《秒杀多线程第五篇经典线程同步关键段CS》《秒杀多线程第六篇经典线程同步事件Event》《秒杀多线程第七篇经典线程同步互斥量Mutex》 ... 查看详情

秒杀多线程第十二篇多线程同步内功心法——pv操作上

阅读本篇之前推荐阅读以下姊妹篇:《秒杀多线程第四篇一个经典的多线程同步问题》《秒杀多线程第五篇经典线程同步关键段CS》《秒杀多线程第六篇经典线程同步事件Event》《秒杀多线程第七篇经典线程同步互斥量Mutex》《秒... 查看详情

秒杀多线程第八篇经典线程同步信号量semaphore

前面介绍了关键段CS、事件Event、互斥量Mutex在经典线程同步问题中的使用。本篇介绍用信号量Semaphore来解决这个问题。首先也来看看如何使用信号量,信号量Semaphore常用有三个函数,使用很方便。下面是这几个函数的原型和使用... 查看详情

秒杀多线程第六篇经典线程同步事件event(代码片段)

阅读本篇之前推荐阅读以下姊妹篇:《秒杀多线程第四篇一个经典的多线程同步问题》《秒杀多线程第五篇经典线程同步关键段CS》 上一篇中使用关键段来解决经典的多线程同步互斥问题,由于关键段的“线程所有权”特性... 查看详情

秒杀多线程第四篇一个经典的多线程同步问题(代码片段)

上一篇《秒杀多线程第三篇原子操作Interlocked系列函数》中介绍了原子操作在多进程中的作用,现在来个复杂点的。这个问题涉及到线程的同步和互斥,是一道非常有代表性的多线程同步问题,如果能将这个问题搞清楚,那么对... 查看详情

线程同步方式之互斥量mutex

互斥量和临界区非常相似,只有拥有了互斥对象的线程才可以访问共享资源,而互斥对象只有一个,因此可以保证同一时刻有且仅有一个线程可以访问共享资源,达到线程同步的目的。互斥量相对于临界区更为高级,可以对互斥... 查看详情

秒杀多线程第一篇多线程笔试面试题汇总

...”,相信你也能对多线程挥洒自如,在笔试面试中顺利的秒杀多 查看详情

线程的互斥锁(代码片段)

一、竞争与同步 当多个线程同时访问其所共享的进程资源时,需要相互协调,以防止出现数据不一致、不完整的问题。这就叫线程同步。 二、互斥量 intpthread_mutex_init(pthread_mutex_t*mutex,constpthread_mutexattr_t*mutexattr); 功能:初始化互... 查看详情

(转载)pthreads线程线程同步--互斥量/锁(代码片段)

pThreads线程(二)线程同步--互斥量/锁  互斥量(Mutex)是“mutualexclusion”的缩写。互斥量是实现线程同步,和保护同时写共享数据的主要方法。  互斥量对共享数据的保护就像一把锁。在Pthreads中,任何时候仅有一个线程可... 查看详情

linux多线程——互斥量实现同步(代码片段)

...0c;拥有lock和unlock两种状态,unlock的互斥锁可以由某个线程获得,当互斥锁由某个线程持有后,这个互斥锁会锁上变成lock状态,此后只有该线程有权力打开该锁,其他想要获得该互斥锁的线程都会阻塞,直... 查看详情

linxu多线程(进程与线程区别,互斥同步)(代码片段)

互斥同步线程线程概念线程优点线程缺点线程异常Linux进程VS线程概念关系线程控制创建线程线程ID及进程地址空间布局线程终止线程等待线程分离互斥背景知识互斥量mutex互斥条件互斥量的接口实例运用(售票系统)互斥... 查看详情

一个经典的多线程同步问题

上一篇《秒杀多线程第三篇原子操作 Interlocked系列函数》中介绍了原子操作在多进程中的作用,现在来个复杂点的。这个问题涉及到线程的同步和互斥,是一道非常有代表性的多线程同步问题,如果能将这个问题搞清楚,那... 查看详情

经典线程同步关键段cs

上一篇《秒杀多线程第四篇一个经典的多线程同步问题》提出了一个经典的多线程同步互斥问题,本篇将用关键段CRITICAL_SECTION来尝试解决这个问题。本文首先介绍下如何使用关键段,然后再深层次的分析下关键段的实现机制与... 查看详情

[c++11多线程同步]---互斥锁(代码片段)

1四种互斥锁在C++11中一共提供了四种互斥锁:std::mutex:独占的互斥锁,不能递归使用std::timed_mutex:带超时的独占互斥锁,不能递归使用std::recursive_mutex:递归互斥锁,不带超时功能std::recursive_timed_mutex:带超时的递归互斥锁互斥... 查看详情

linux多线程——互斥和同步(代码片段)

目录一.线程互斥    1.1相关概念    1.2互斥量mutex    1.3互斥量的接口    1.4总结    1.5互斥锁实现原理(锁的原理)二.可重入函数和线程安全    2.1概念三.死锁         3.1概念    3.2死锁的必要条件        3.... 查看详情

c11线程管理:互斥锁

1、概述  锁类型  c11提供了跨平台的线程同步手段,用来保护多线程同时访问的共享数据。  std::mutex,最基本的Mutex类,独占的互斥量,不能递归使用。  std::time_mutex,带超时的独占互斥量,不能递归使用。  std::rec... 查看详情

秒杀多线程第十篇生产者消费者问题(代码片段)

   继经典线程同步问题之后,我们来看看生产者消费者问题及读者写者问题。生产者消费者问题是一个著名的线程同步问题,该问题描述如下:有一个生产者在生产产品,这些产品将提供给若干个消费者去消费,为了... 查看详情

c++11——线程库,互斥量,原子性库,条件变量(代码片段)

目录前言一.线程库    1.线程库接口        2.使用     2.1线程函数    2.2线程函数的参数     2.3join和detach介绍二.互斥量介绍    1.互斥量种类   1.mutex最基本Mutex类    2.recursive_mutex递归Mutex类    3.timed_mutex定... 查看详情