C++ 经典线程同步互斥量Mutex 示例解析(十二)

2024-05-09 18:18

本文主要是介绍C++ 经典线程同步互斥量Mutex 示例解析(十二),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在windows系统中,系统本身为我们提供了很多锁。通过这些锁的使用,一方面可以加强我们对锁的认识,另外一方面可以提高代码的性能和健壮性。常用的锁以下四种:

临界区:C++ 关键段(Critical Section)CS深入浅出 之多线程(七)

event :C++ 经典线程同步 事件Event(九)

信号量:信号量是使用的最多的一种锁结果,也是最方便的一种锁。围绕着信号量,人们提出了很多数据互斥访问的方案,pv操作就是其中的一种。如果说互斥锁只能对单个资源进行保护,那么信号量可以对多个资源进行保护。同时信号量在解锁的时候,可以被另外一个thread进行解锁操作。

互斥量:将是这篇文章所接触到的!

ok!本次Demo参考于MSDN:MSDN

1 首先来熟悉我们将要接触到的系列函数

CreateMutex:

HANDLE WINAPI CreateMutex(_In_opt_  LPSECURITY_ATTRIBUTES lpMutexAttributes,_In_      BOOL bInitialOwner,_In_opt_  LPCTSTR lpName
);

函数参数说明:(CreateMutex:)

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

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

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

函数访问值:

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

备注:通过CreateMutex返回的句柄有MUTEX_ALL_ACCESS访问权,它可以在需要的句柄互斥对象的任何函数中使用,前提是调用者已被授予访问权限。如果一个互斥体是由一个服务或一个线程正在模拟不同的用户创建的,你可以申请一个安全描述符来互斥,当你创建它,或者通过改变其默认的DACL更改为创建进程的默认安全描述符

OpenMutex:

HANDLE OpenMutex(
DWORDdwDesiredAccess, // access
BOOLbInheritHandle, // inheritance option
LPCTSTRlpName // object name
);

参数说明:

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

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

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

函数访问值:成功返回一个表示互斥量的句柄,失败返回NULL


ReleaseMutex:

BOOL WINAPI ReleaseMutex(_In_  HANDLE hMutex
);

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


给出CSDN的demo,demo中也有比较详细的注释!

#include <windows.h>
#include <stdio.h>#define THREADCOUNT 4 HANDLE ghWriteEvent; 
HANDLE ghThreads[THREADCOUNT];DWORD WINAPI ThreadProc(LPVOID);void CreateEventsAndThreads(void) 
{int i; DWORD dwThreadID; // Create a manual-reset event object. The write thread sets this// object to the signaled state when it finishes writing to a // shared buffer. ghWriteEvent = CreateEvent( NULL,               // default security attributesTRUE,               // manual-reset eventFALSE,              // initial state is nonsignaledTEXT("WriteEvent")  // object name); if (ghWriteEvent == NULL) { printf("CreateEvent failed (%d)\n", GetLastError());return;}// Create multiple threads to read from the buffer.for(i = 0; i < THREADCOUNT; i++) {// TODO: More complex scenarios may require use of a parameter//   to the thread procedure, such as an event per thread to  //   be used for synchronization.ghThreads[i] = CreateThread(NULL,              // default security0,                 // default stack sizeThreadProc,        // name of the thread functionNULL,              // no thread parameters0,                 // default startup flags&dwThreadID); if (ghThreads[i] == NULL) {printf("CreateThread failed (%d)\n", GetLastError());return;}}
}void WriteToBuffer(VOID) 
{// TODO: Write to the shared buffer.printf("Main thread writing to the shared buffer...\n");// Set ghWriteEvent to signaledif (! SetEvent(ghWriteEvent) ) {printf("SetEvent failed (%d)\n", GetLastError());return;}
}void CloseEvents()
{// Close all event handles (currently, only one global handle).CloseHandle(ghWriteEvent);
}int main( void )
{DWORD dwWaitResult;// TODO: Create the shared buffer// Create events and THREADCOUNT threads to read from the bufferCreateEventsAndThreads();// At this point, the reader threads have started and are most// likely waiting for the global event to be signaled. However, // it is safe to write to the buffer because the event is a // manual-reset event.WriteToBuffer();printf("Main thread waiting for threads to exit...\n");// The handle for each thread is signaled when the thread is// terminated.dwWaitResult = WaitForMultipleObjects(THREADCOUNT,   // number of handles in arrayghThreads,     // array of thread handlesTRUE,          // wait until all are signaledINFINITE);switch (dwWaitResult) {// All thread objects were signaledcase WAIT_OBJECT_0: printf("All threads ended, cleaning up for application exit...\n");break;// An error occurreddefault: printf("WaitForMultipleObjects failed (%d)\n", GetLastError());return 1;} // Close the events to clean upCloseEvents();system("pause");return 0;
}DWORD WINAPI ThreadProc(LPVOID lpParam) 
{// lpParam not used in this example.UNREFERENCED_PARAMETER(lpParam);DWORD dwWaitResult;printf("Thread %d waiting for write event...\n", GetCurrentThreadId());dwWaitResult = WaitForSingleObject( ghWriteEvent, // event handleINFINITE);    // indefinite waitswitch (dwWaitResult) {// Event object was signaledcase WAIT_OBJECT_0: //// TODO: Read from the shared buffer//printf("Thread %d reading from buffer\n", GetCurrentThreadId());break; // An error occurreddefault: printf("Wait error (%d)\n", GetLastError()); return 0; }// Now that we are done reading the buffer, we could use another// event to signal that this thread is no longer reading. This// example simply uses the thread handle for synchronization (the// handle is signaled when the thread terminates.)printf("Thread %d exiting\n", GetCurrentThreadId());return 1;
}

运行效果如下:


最后总结下互斥量Mutex

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

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


这篇关于C++ 经典线程同步互斥量Mutex 示例解析(十二)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/974134

相关文章

线上Java OOM问题定位与解决方案超详细解析

《线上JavaOOM问题定位与解决方案超详细解析》OOM是JVM抛出的错误,表示内存分配失败,:本文主要介绍线上JavaOOM问题定位与解决方案的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录一、OOM问题核心认知1.1 OOM定义与技术定位1.2 OOM常见类型及技术特征二、OOM问题定位工具

C++右移运算符的一个小坑及解决

《C++右移运算符的一个小坑及解决》文章指出右移运算符处理负数时左侧补1导致死循环,与除法行为不同,强调需注意补码机制以正确统计二进制1的个数... 目录我遇到了这么一个www.chinasem.cn函数由此可以看到也很好理解总结我遇到了这么一个函数template<typename T>unsigned

C++统计函数执行时间的最佳实践

《C++统计函数执行时间的最佳实践》在软件开发过程中,性能分析是优化程序的重要环节,了解函数的执行时间分布对于识别性能瓶颈至关重要,本文将分享一个C++函数执行时间统计工具,希望对大家有所帮助... 目录前言工具特性核心设计1. 数据结构设计2. 单例模式管理器3. RAII自动计时使用方法基本用法高级用法

Python中logging模块用法示例总结

《Python中logging模块用法示例总结》在Python中logging模块是一个强大的日志记录工具,它允许用户将程序运行期间产生的日志信息输出到控制台或者写入到文件中,:本文主要介绍Pyt... 目录前言一. 基本使用1. 五种日志等级2.  设置报告等级3. 自定义格式4. C语言风格的格式化方法

Spring 中的切面与事务结合使用完整示例

《Spring中的切面与事务结合使用完整示例》本文给大家介绍Spring中的切面与事务结合使用完整示例,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考... 目录 一、前置知识:Spring AOP 与 事务的关系 事务本质上就是一个“切面”二、核心组件三、完

深度解析Python中递归下降解析器的原理与实现

《深度解析Python中递归下降解析器的原理与实现》在编译器设计、配置文件处理和数据转换领域,递归下降解析器是最常用且最直观的解析技术,本文将详细介绍递归下降解析器的原理与实现,感兴趣的小伙伴可以跟随... 目录引言:解析器的核心价值一、递归下降解析器基础1.1 核心概念解析1.2 基本架构二、简单算术表达

深度解析Java @Serial 注解及常见错误案例

《深度解析Java@Serial注解及常见错误案例》Java14引入@Serial注解,用于编译时校验序列化成员,替代传统方式解决运行时错误,适用于Serializable类的方法/字段,需注意签... 目录Java @Serial 注解深度解析1. 注解本质2. 核心作用(1) 主要用途(2) 适用位置3

sky-take-out项目中Redis的使用示例详解

《sky-take-out项目中Redis的使用示例详解》SpringCache是Spring的缓存抽象层,通过注解简化缓存管理,支持Redis等提供者,适用于方法结果缓存、更新和删除操作,但无法实现... 目录Spring Cache主要特性核心注解1.@Cacheable2.@CachePut3.@Ca

QT Creator配置Kit的实现示例

《QTCreator配置Kit的实现示例》本文主要介绍了使用Qt5.12.12与VS2022时,因MSVC编译器版本不匹配及WindowsSDK缺失导致配置错误的问题解决,感兴趣的可以了解一下... 目录0、背景:qt5.12.12+vs2022一、症状:二、原因:(可以跳过,直奔后面的解决方法)三、解决方

Java中如何正确的停掉线程

《Java中如何正确的停掉线程》Java通过interrupt()通知线程停止而非强制,确保线程自主处理中断,避免数据损坏,线程池的shutdown()等待任务完成,shutdownNow()强制中断... 目录为什么不强制停止为什么 Java 不提供强制停止线程的能力呢?如何用interrupt停止线程s