同步控制中Mutex互斥器Critical Section 临界会话的区别

转载: http://blog.youkuaiyun.com/webols/article/details/6820028

同步控制中Mutex互斥器Critical Section 临界会话的区别

Win32的Mutex和Critical Seciton非常类似。但是他以牺牲速度来增加弹性。Mutex是核心对象

Mutex 和Critical Section 的区别:

  • 锁住一个未被拥有的Mutex,比锁住一个未被拥有的Critical Section需要花费几乎100倍的时间 。
  • Mutex可以跨进程使用。 Critical Section只能在一个进程中使用。
  • 等待一个Mutex,你可以指定“结束等待” 的时间长短。对于critical section 就不行。

 两种对象的相关函数比较:

 CRITICAL_SECTION                              Mutex核心对象

InitializeCriticalSection()          CreateMutex(), OpenMutex()

EnterCriticalSection()              WaitForSingleObject(), WaitForMultipleObject(), MsgWaitForMultipleObject()

LeaveCriticalSection()            ReleaseMutex()

DeleteCriticalSection()          CloseHandle()

  

为了能跨进程使用mutex,应该在产生mutex时指定名称。那么系统的其他进程就可以使用名称来处理这个mutex。

基本知识:

产生一个互斥器

Creates or opens a named or unnamed mutex  object .To specify an access mask  for  the  object , use the CreateMutexEx function.

HANDLE WINAPI CreateMutex(
  __in          LPSECURITY_ATTRIBUTES lpMutexAttributes, 
// 安全属性,NULL表默认
  __in          BOOL bInitialOwner,     // 如果你希望“调用CreateMutex”的这个线程拥有产生出来的mutex,就将此值设为TRUE。
  __in          LPCTSTR lpName       //mutex名称
);
返回值:如果成功,返回一个handle,否则返回NULL,调用GetLastError获取进一步的信息。如果指定的mutex名称已存在,GetLastError()会传回ERROR_ALREADY_EXISTS.
 

当不需要mutex时,调用CloseHandle关闭它。

 打开一个互斥器(mutex)

任何进程和线程都可以根据名称打开一个已经存在的mutex。

如果 你调用createmutex并指定了一个已经存在的mutex名称。win32会返回给你一个mutex的handle。而不会产生一个mutex,getlasterror回传会 ERROR_ALREADY_EXISTS

锁住一个互斥器(mutex) 

要获取mutex的拥有权,使用wait..。系列函数。wait。。()对mutex所作的事情类似于enterCriticalSection对CriticalSection做的事情。一旦没有任何线程拥有mutex,它就处于激发状态,wait...就会成功。

释放一个互斥器(mutex) 

获得mutex的线程调用ReleaseMutex,将之释放,使mutex处于激发状态。

 

Releases ownership of the specified mutex  object .

BOOL WINAPI ReleaseMutex(
  __in          HANDLE hMutex  
// 欲释放之mutex的handle
);

返回值:成功TRUE,失败FALSE

处理被舍弃的互斥器(Mutex)

如果线程拥有一个mutex而在结束前没有调用ReleaseMutex,mutex不会被摧毁,其会被视为“未被拥有”以及“未被激发”,而下一个等待中的线程会被以WAIT_ABANDONED_0通知,不论线程是应为ExitThread而结束,或是当掉而结束。

如果其他线程正以WaitForMultipleObjects()等待此mutex,该函数也会返回,传回值介于 WAIT_ABANDONED_0和WAIT_ABANDONED_0_n+1之间,其中n指handle数组的元素个数。线程可以以此来了解那个mutex被放弃了。至于WaitForSingleOBject,只返回WAIT_ABANDONED_0

为什么有一个最初的拥有者 

 CreateMutex的第二个参数允许你指定现行线程是否立刻拥有即将产生的mutex,他是为了阻止race condition的发生。


参考《Win32多线程程序设计》

<think>好的,我现在需要帮助用户解决在Windows环境下使用C语言如何应用互斥锁的问题。用户之前已经询问过线程和事件的区别,现在进一步深入多线程编程中的同步机制。这说明用户可能正在开发一个多线程程序,遇到了资源竞争或数据一致性的问题,需要利用互斥锁来解决。 首先,我要确定用户的基本需求。他们可能已经了解线程和事件的基本概念,现在需要具体的实现方法。互斥锁(Mutex)是用于确保同一时间只有一个线程访问共享资源的机制,防止数据竞争。在Windows中,相关的API函数包括CreateMutex、WaitForSingleObject、ReleaseMutex和CloseHandle。 接下来,我需要考虑用户的背景。他们可能具备一定的C语言基础,但对Windows API不太熟悉。因此,回答需要详细解释每个函数的作用、参数以及使用步骤,并提供代码示例。同时,要提醒常见的错误和最佳实践,比如检查返回值、避免死锁、及时释放互斥体等。 用户可能希望了解如何创建互斥锁、如何在不同的线程中获取和释放锁,以及如何处理错误情况。还需要注意跨进程同步的情况,因为CreateMutex的命名互斥体可以用于不同进程之间的同步,这可能超出用户当前需求,但有必要提及。 另外,用户可能对互斥锁和其他同步机制(如事件、临界区)的区别不太清楚,但根据问题范围,应专注于互斥锁的具体使用方法。需要确保示例代码简洁明了,注释详细,帮助用户理解每一步的操作。 最后,要总结关键点,强调正确使用互斥锁的重要性,避免资源泄漏和死锁,确保程序稳定运行。可能还需要指出与CRITICAL_SECTION区别,帮助用户根据具体需求选择合适的同步机制。</think>在Windows系统下使用C语言操作互斥锁(Mutex)时,主要通过Windows API实现线程同步。以下是具体使用指南,包含关键API说明和典型应用场景: --- ### 一、核心API函数 | 函数名 | 参数说明 | 返回值 | |-------------------------|--------------------------------------------------------------------------|----------------------| | `CreateMutex` | `lpMutexAttributes`: 安全属性<br>`bInitialOwner`: 初始所有权<br>`lpName`: 互斥体名称 | 互斥体句柄(失败返回NULL) | | `WaitForSingleObject` | `hHandle`: 互斥体句柄<br>`dwMilliseconds`: 超时时间(INFINITE表示永久等待) | WAIT_OBJECT_0等状态码 | | `ReleaseMutex` | `hMutex`: 要释放的互斥体句柄 | BOOL类型成功状态 | | `CloseHandle` | `hObject`: 要关闭的互斥体句柄 | BOOL类型成功状态 | --- ### 二、基础使用步骤 #### 1. 创建互斥体 ```c HANDLE hMutex = CreateMutex( NULL, // 默认安全属性 FALSE, // 初始不拥有所有权 NULL // 匿名互斥体 ); if (hMutex == NULL) { DWORD err = GetLastError(); // 错误处理(例如输出错误码) } ``` #### 2. 请求互斥体所有权 ```c DWORD waitResult = WaitForSingleObject( hMutex, // 互斥体句柄 INFINITE // 无限等待 ); switch (waitResult) { case WAIT_OBJECT_0: // 成功获得所有权 // 访问共享资源 break; case WAIT_ABANDONED: // 前一个所有者异常终止 // 处理异常状态 break; case WAIT_FAILED: // 系统错误 // 错误处理 break; } ``` #### 3. 释放互斥体 ```c if (!ReleaseMutex(hMutex)) { DWORD err = GetLastError(); // 错误处理(例如:非所有者线程尝试释放) } ``` #### 4. 关闭句柄 ```c CloseHandle(hMutex); // 程序退出前必须执行 ``` --- ### 三、典型应用场景 #### 场景1:银行账户转账保护 ```c // 共享资源 double accountBalance = 10000.0; DWORD WINAPI TransferThread(LPVOID lpParam) { HANDLE hMutex = (HANDLE)lpParam; WaitForSingleObject(hMutex, INFINITE); // 临界区开始 accountBalance += 500.0; // 存款操作 accountBalance -= 300.0; // 取款操作 // 临界区结束 ReleaseMutex(hMutex); return 0; } int main() { HANDLE hMutex = CreateMutex(NULL, FALSE, NULL); // 创建多个线程进行转账操作... } ``` #### 场景2:跨进程日志写入 ```c // 进程A HANDLE hMutex = CreateMutex(NULL, FALSE, TEXT("Global\\MyLogMutex")); // 进程B HANDLE hMutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, TEXT("Global\\MyLogMutex")); // 写入日志文件前 WaitForSingleObject(hMutex, INFINITE); WriteToLogFile("Operation completed"); ReleaseMutex(hMutex); ``` --- ### 四、关键注意事项 1. **异常处理** - 每个API调用后都应检查返回值 - 使用`GetLastError()`获取详细错误码 2. **死锁防范** ```c // 设置超时避免永久阻塞 DWORD result = WaitForSingleObject(hMutex, 5000); // 5秒超时 if (result == WAIT_TIMEOUT) { // 处理超时逻辑 } ``` 3. **所有权规则** - 只有获得所有权的线程才能释放互斥体 - 避免在未获得所有权时调用`ReleaseMutex` 4. **命名规范** - 跨进程使用时需遵循命名规则 - 建议使用`Global\\`前缀实现会话间共享 --- ### 五、与CRITICAL_SECTION对比 | **特性** | **Mutex** | **CRITICAL_SECTION** | |--------------------|-------------------------------|----------------------------| | 跨进程同步 | ✔️ | ❌ | | 递归获取 | ✔️(同线程可重复获取) | ✔️ | | 超时设置 | ✔️(通过WaitFor*函数) | ❌ | | 性能 | 较高系统开销(约1000周期) | 较低开销(约40周期) | | 异常终止处理 | 自动释放所有权(WAIT_ABANDONED)| 需手动清理 | --- ### 六、最佳实践建议 1. **资源释放** 始终在`__try/__finally`块中确保释放: ```c __try { WaitForSingleObject(hMutex, INFINITE); // 操作共享资源 } __finally { ReleaseMutex(hMutex); } ``` 2. **避免嵌套过深** 单线程内获取互斥体次数应与释放次数严格匹配: ```c WaitForSingleObject(hMutex, INFINITE); // 第一次获取 WaitForSingleObject(hMutex, INFINITE); // 第二次获取(合法但需对应两次释放) ``` 3. **调试辅助** 使用`Wait Chain Traversal (WCT)`工具检测死锁: ```powershell windbg -c "!wct" ``` 通过合理使用互斥锁,可以确保在多线程环境中实现安全的数据访问,但要注意平衡同步粒度和性能开销。对于高频访问的临界区,建议优先考虑轻量级的CRITICAL_SECTION
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值