1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > 【学习笔记】Windows 下线程同步之互斥锁

【学习笔记】Windows 下线程同步之互斥锁

时间:2019-11-14 17:55:38

相关推荐

【学习笔记】Windows 下线程同步之互斥锁

目录

前言环境简介相关函数CreateMutexWait 函数ReleaseMutexCloseHandle 其他互斥锁的名字未命名互斥锁的同步互斥锁的意外终止临界区对象 示例参考

前言

本文所涉及的同步主要描述在 Windows 环境下的机制,和 Linux 中的同步机制有一定的联系,但注意并不完全相同。类似于,Windows 和 Linux 按照自己的方式实现了操作系统中同步机制的概念本文记录的是 Windows 下的互斥锁同步机制,但在 Windows 的同步机制中,其中很多的概念和逻辑同样适用于事件(Event),信号量,计时器等其他同步机制

环境

OS:winIDE:Visual Studio

简介

简介:互斥锁是一种同步对象,当没有任何线程拥有互斥锁时,互斥锁处于有信号(signaled)状态,当互斥锁被某个线程拥有,则它处于无信号状态(nonsignaled)。

顾名思义,互斥锁就是一种为了达到访问共享资源而互斥目的的锁。比如生活中,公共厕所就是一种共享资源,公厕一次只能有一个人使用,使用者在使用的时候就会关门上锁,使用完之后需要开门释放锁。对于每个使用者来说,这个锁一次只能被一个人占有

特点

任何一个互斥锁,一次只能被一个线程拥有可以跨进程使用,即进程间同步

适用场景:同步一些共享资源,比如共享内存(shared memory)

相关函数

CreateMutex

作用:创建或打开命名或未命名的互斥锁对象。如果某互斥锁已经被创建,当再次使用CreateMutex操做该互斥锁,实际的操作等效于OpenMutex,但通过GetLastError会返回ERROR_ALREADY_EXISTS标识语法

HANDLE CreateMutexA([in, optional] LPSECURITY_ATTRIBUTES lpMutexAttributes,[in] BOOL bInitialOwner,[in, optional] LPCSTRlpName);

参数 lpMutexAttributes,为 NULL 时,句柄不能被子进程继承bInitialOwner,为 true 时,创建该互斥锁的线程获取该互斥锁lpName,互斥锁的名字,为 NULL 时,为未命名互斥锁,关于未命名互斥锁如何传递见下方“未命名互斥锁的同步”

Wait 函数

Wait 函数是一系列提供类似功能的等待函数(如WaitForMultipleObjects),该函数的作用是请求某个互斥锁的使用权,若没有获取到,则阻塞等待函数的返回值表明等待函数因为某些原因返回,而不是正常的互斥锁信号转换多个线程等待互斥锁时,只有一个线程会被随机选择获取互斥锁

ReleaseMutex

作用:释放控制权,释放后,互斥锁变为有信号状态语法

BOOL ReleaseMutex([in] HANDLE hMutex);

参数:hMutex 是要释放的互斥锁句柄

CloseHandle

作用:关闭句柄,本文中即关闭互斥锁

【注】除了使用CloseHandle手动关闭句柄外,当某个进程终止后,也会自动关闭句柄

【注】CloseHandle关闭当前线程中使用的句柄,但如果还有其他线程拥有该句柄,那么句柄对象并未真正关闭,只有当最后一个该对象被关闭,句柄才会真正关闭

如图,只有当所有句柄均被关闭,互斥锁对象才会自动关闭

其他

互斥锁的名字

特别注意的是,互斥锁的名字和其他同步对象(如,事件,信号量)的名字位于相同的命名空间,因此如果互斥锁有名字为“ExampleName”,事件也有名字为“ExampleName”则发生冲突,通过GetLastError函数返回ERROR_INVALID_HANDLE错误。

更多关于内核对象命名空间的知识可以阅读参考中的链接。

未命名互斥锁的同步

命名的互斥锁我们可以很容易理解如何让两个或多个线程使用相同的互斥锁。而未命名的互斥锁要如何让系统中多个线程与同一个互斥锁产生联系呢,答案是通过线程间(或进程间)复制句柄或者父子句柄继承实现,这里我们主要讲一下复制句柄

复制句柄:通过该方法可以在两个进程之间传递句柄,但相比于命名句柄和父子继承的方式,这种方式是最麻烦的,它需要在创建句柄的进程和使用句柄的进程间进行通信(如,命名管道,命名共享内存),当然这一步 Windows 通过高层函数隐藏了底层实现的细节,也就是说,你只需要调用一个DuplicateHandle函数即可完成两个进程的通信。

另一个需要注意的地方,复制的句柄本质上和源句柄是等同的,你可以理解为指针间的赋值,赋值过后的两个指针实际是指向的相同的区域,任何改变都会影响两个指针指向的区域,句柄也是如此。

语法

BOOL DuplicateHandle([in] HANDLE hSourceProcessHandle,[in] HANDLE hSourceHandle,[in] HANDLE hTargetProcessHandle,[out] LPHANDLE lpTargetHandle,[in] DWORD dwDesiredAccess,[in] BOOLbInheritHandle,[in] DWORD dwOptions);

参数 hSourceProcessHandle:源进程的句柄,该进程必须有PROCESS_DUP_HANDLE的接入权限。源句柄可以通过 GetCurrentProcess 获得hSourceHandle:要被复制的句柄,比如互斥锁句柄hTargetProcessHandle:目标进程的句柄,该进程必须有PROCESS_DUP_HANDLE的接入权限。目标句柄可以通过 OpenProcess 获得lpTargetHandle:注意它是一个指针,指向复制过来的句柄,“LP” 是 long pointer 的缩写dwDesiredAccess:新句柄的权限设置。一般通过复制得到的句柄的权限范围 ≤ 原句柄bInheritHandle:该句柄能否被继承dwOptions:一些可选的配置项,这里不展开

关于句柄复制并非本篇的重点,详细内容移步官方文档

互斥锁的意外终止

比如,当前拥有互斥锁的线程终止,而该线程并未释放互斥锁,此时互斥锁被标记为遗弃(abandoned),它表明互斥锁未被正确释放

其他等待该互斥锁的线程可以获取它,但对应的wait函数会返回WAIT_ABANDONED来表明互斥锁对象被遗弃,由此表明此时被互斥锁操控的共享资源处于未定义状态(undefined state)

临界区对象

临界区(critical section)对象提供类似与互斥锁的功能,区别在于临界区对象不提供进程间同步,只能提供同一进程中的线程的同步

【注】这里的临界区对象是 Windows 提供的一种用户模式下的线程同步机制,不完全等同于操作系统中的临界区这个概念

示例

在本示例中,我们启动两个进程,分别为 A process 和 B process。

A process 产生一个互斥锁,名为 MutexDemo,并且在产生时就获得该锁的使用权,之后就是执行使用共享资源的代码,然后释放互斥锁,最终关闭互斥锁句柄。

B process 打开名为 MutexDemo,获得 MutexDemo 的使用权后,执行使用共享资源的代码,然后释放互斥锁,最终关闭互斥锁句柄。

A process cpp

void MutexSynchronize(){cout << "A thread: enter mutex creator" << endl;// create a mutex and initially get itHANDLE hMutex = CreateMutex(NULL, true, _TEXT("MutexDemo"));// Simulate execute business logiccout << "A thread: set data into shared memory" << endl;// release mutex, mutex becomes signaledReleaseMutex(hMutex);// close handleSleep(5000);// wait B thread get mutex, because we cannot predict the execution order // of multiple processesCloseHandle(hMutex);}

B process cpp

void MutexSynchronize(){cout << "B thread: enter mutex opener" << endl;HANDLE hMutex = NULL;// wait A thread create mutex and open itwhile (hMutex == NULL){hMutex = OpenMutex(MUTEX_ALL_ACCESS, false, _TEXT("MutexDemo"));cout << "B thread: wait mutex" << endl;}// wait signaled mutexWaitForSingleObject(hMutex, INFINITE);// Simulate execute business logiccout << "B thread: get data from shared memory" << endl;// release mutex, mutex becomes signaledReleaseMutex(hMutex);// close handleCloseHandle(hMutex);}

参考

Synchronization《Windows 核心编程》Process Security and Access RightsThread Handles and Identifiers:what is pseudo handle

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。