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

这篇具有很好参考价值的文章主要介绍了【学习笔记】Windows 下线程同步之互斥锁。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

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

环境

  1. OS:win
  2. IDE:Visual Studio 2015

简介

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

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

  • 特点

    • 任何一个互斥锁,一次只能被一个线程拥有
    • 可以跨进程使用,即进程间同步
  • 适用场景:同步一些共享资源,比如共享内存(shared memory)

相关函数

CreateMutex

  1. 作用:创建或打开命名或未命名的互斥锁对象。如果某互斥锁已经被创建,当再次使用 CreateMutex 操做该互斥锁,实际的操作等效于 OpenMutex,但通过 GetLastError 会返回 ERROR_ALREADY_EXISTS 标识
  2. 语法
HANDLE CreateMutexA(
  [in, optional] LPSECURITY_ATTRIBUTES lpMutexAttributes,
  [in]           BOOL                  bInitialOwner,
  [in, optional] LPCSTR                lpName
);
  1. 参数
    1. lpMutexAttributes,为 NULL 时,句柄不能被子进程继承
    2. bInitialOwner,为 true 时,创建该互斥锁的线程获取该互斥锁
    3. lpName,互斥锁的名字,为 NULL 时,为未命名互斥锁,关于未命名互斥锁如何传递见下方“未命名互斥锁的同步”

Wait 函数

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

ReleaseMutex

  1. 作用:释放控制权,释放后,互斥锁变为有信号状态
  2. 语法
BOOL ReleaseMutex(
  [in] HANDLE hMutex
);
  1. 参数:hMutex 是要释放的互斥锁句柄

CloseHandle

  1. 作用:关闭句柄,本文中即关闭互斥锁
    【注】除了使用 CloseHandle 手动关闭句柄外,当某个进程终止后,也会自动关闭句柄
    【注】CloseHandle 关闭当前线程中使用的句柄,但如果还有其他线程拥有该句柄,那么句柄对象并未真正关闭,只有当最后一个该对象被关闭,句柄才会真正关闭
    【学习笔记】Windows 下线程同步之互斥锁

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

其他

互斥锁的名字

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

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

未命名互斥锁的同步

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

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

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

  3. 语法

BOOL DuplicateHandle(
  [in]  HANDLE   hSourceProcessHandle,
  [in]  HANDLE   hSourceHandle,
  [in]  HANDLE   hTargetProcessHandle,
  [out] LPHANDLE lpTargetHandle,
  [in]  DWORD    dwDesiredAccess,
  [in]  BOOL     bInheritHandle,
  [in]  DWORD    dwOptions
);
  1. 参数
    1. hSourceProcessHandle:源进程的句柄,该进程必须有 PROCESS_DUP_HANDLE 的接入权限。源句柄可以通过 GetCurrentProcess 获得
    2. hSourceHandle:要被复制的句柄,比如互斥锁句柄
    3. hTargetProcessHandle:目标进程的句柄,该进程必须有 PROCESS_DUP_HANDLE 的接入权限。目标句柄可以通过 OpenProcess 获得
    4. lpTargetHandle:注意它是一个指针,指向复制过来的句柄,“LP” 是 long pointer 的缩写
    5. dwDesiredAccess:新句柄的权限设置。一般通过复制得到的句柄的权限范围 ≤ 原句柄
    6. bInheritHandle:该句柄能否被继承
    7. 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 it
	HANDLE hMutex = CreateMutex(NULL, true, _TEXT("MutexDemo"));

	// Simulate execute business logic
	cout << "A thread: set data into shared memory" << endl;

	// release mutex, mutex becomes signaled
	ReleaseMutex(hMutex);

	// close handle
	Sleep(5000);		// wait B thread get mutex, because we cannot predict the execution order 
				     	// of 	multiple processes
	CloseHandle(hMutex);
}

B process cpp文章来源地址https://www.toymoban.com/news/detail-462221.html

void MutexSynchronize()
{
	cout << "B thread: enter mutex opener" << endl;
	HANDLE hMutex = NULL;

	// wait A thread create mutex and open it
	while (hMutex == NULL)
	{
		hMutex = OpenMutex(MUTEX_ALL_ACCESS, false, _TEXT("MutexDemo"));
		cout << "B thread: wait mutex" << endl;
	}

	// wait signaled mutex
	WaitForSingleObject(hMutex, INFINITE);

	// Simulate execute business logic
	cout << "B thread: get data from shared memory" << endl;

	// release mutex, mutex becomes signaled
	ReleaseMutex(hMutex);

	// close handle
	CloseHandle(hMutex);
}

参考

  1. Synchronization
  2. 《Windows 核心编程》
  3. Process Security and Access Rights
  4. Thread Handles and Identifiers:what is pseudo handle

到了这里,关于【学习笔记】Windows 下线程同步之互斥锁的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Linux】线程同步和互斥

    1.临界资源:多线程执行流共享的资源,且一次只能允许一个执行流访问的资源就叫做临界资源。(多线程、多进程打印数据) 2.临界区:每个线程内部,访问临界资源的代码,就叫做临界区。 3.互斥:任何时刻,互斥保证有且只有一个执行流进入临界区,访问临界资源,通常对

    2024年02月08日
    浏览(42)
  • 线程同步与互斥

    目录 前言:基于多线程不安全并行抢票 一、线程互斥锁 mutex 1.1 加锁解锁处理多线程并发  1.2 如何看待锁 1.3 如何理解加锁解锁的本质 1.4 C++RAII方格设计封装锁 前言:基于线程安全的不合理竞争资源 二、线程同步 1.1 线程同步处理抢票 1.2 如何理解\\\"条件变量\\\" 1.3 如何理解条

    2024年02月10日
    浏览(34)
  • 【Linux】多线程互斥与同步

    互斥 指的是一种机制,用于确保在同一时刻只有一个进程或线程能够访问共享资源或执行临界区代码。 互斥的目的是 防止多个并发执行的进程或线程访问共享资源时产生竞争条件,从而保证数据的一致性和正确性 ,下面我们来使用多线程来模拟实现一个抢票的场景,看看所

    2024年02月09日
    浏览(38)
  • Linux——多线程,互斥与同步

    目录 一.linux互斥 1.进程线程间的互斥相关背景概念 2.互斥量mutex 3.加锁互斥锁mutex 4.锁的底层原理  二.可重入VS线程安全 1.概念 2.常见的线程不安全的情况 3.常见的线程安全的情况  4.常见不可重入的情况  5..常见可重入的情况 6.可重入与线程安全联系  三.死锁 1.死锁四个必

    2024年02月05日
    浏览(33)
  • Linux——线程的同步与互斥

    目录 模拟抢火车票的过程 代码示例 thread.cc Thread.hpp 运行结果 分析原因 tickets减到-2的本质  解决抢票出错的方案 临界资源的概念 原子性的概念 加锁 定义 初始化 销毁 代码形式如下 代码示例1: 代码示例2: 总结 如何看待锁 申请失败将会阻塞  pthread_mutex_tyrlock 互斥锁实现

    2024年02月06日
    浏览(39)
  • Linux-线程的同步与互斥

    🚀 临界资源:多线程指行流共享的资源叫做临界资源。 🚀 临界区:每个线程内部访问临界资源的代码片段叫做临界区。 🚀 互斥:任何时刻,互斥保证只有一个指行流进入临界区,访问临界资源,通常是对临界区起保护作用。 🚀 原子性:不被任何调度所打断的操作,该

    2024年02月09日
    浏览(46)
  • 【Linux】多线程2——线程互斥与同步/多线程应用

    💭上文主要介绍了多线程之间的独立资源,本文将详细介绍多线程之间的 共享资源 存在的问题和解决方法。 intro 多线程共享进程地址空间,包括创建的全局变量、堆、动态库等。下面是基于全局变量实现的一个多线程抢票的demo。 发现错误:线程抢到负数编号的票,为什么

    2024年02月10日
    浏览(42)
  • 【Linux】多线程 --- 线程同步与互斥+生产消费模型

    人生总是那么痛苦吗?还是只有小时候是这样? —总是如此 1. 假设现在有一份共享资源tickets,如果我们想让多个线程都对这个资源进行操作,也就是tickets- -的操作,但下面两份代码分别出现了不同的结果,上面代码并没有出现问题,而下面代码却出现了票为负数的情况,这

    2024年02月06日
    浏览(43)
  • 【Linux】Linux线程互斥与同步

    临界资源:多线程执行流共享的资源就叫做临界资源 临界区:每个线程内部,访问临界资源的代码,就叫做临界区 互斥:任何时刻,互斥保证有且只有一个执行流进入临界区,访问临界资源,通常对临界资源起保护作用 原子性:不会被任何调度机制打断的操作,该操作只有

    2024年02月04日
    浏览(39)
  • 【关于Linux中----线程互斥与同步】

    先来用代码模拟一个抢票的场景,四个线程不停地抢票,一共有1000张票,抢完为止,代码如下: 执行结果如下: 可以看到,最后出现了票数为负数的情况,很显然这是错误的,是不应该出现的。 为什么会出现这种情况? 首先要明确,上述的几个线程是不能同时执行抢票的

    2023年04月08日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包