Mutex的lock(), tryLock()区别

本文介绍了lock函数和tryLock函数的使用方式及其区别。lock函数通过无限等待实现阻塞锁定,而tryLock函数则尝试立即获取锁而不等待。两者均需调用unlock函数解锁。

lock函数和tryLock函数都是用于锁定对象,但他们之间有一定的区别:

lock函数是阻塞的,因为它调用WaitForSingleObject函数时传递的第二个参数是INFINITE,表示无限等待下去,所以是阻塞的。

tryLock函数时非阻塞的,调用后立即返回。因为它调用WaitForSingleObject函数时传递的第二个参数是0,表示不等待,立即返回。

调用lock或者tryLock后,都需要调用unlock来解锁。
  1. status_t Mutex::lock()  
  2. {  
  3.     DWORD dwWaitResult;  
  4.     dwWaitResult = WaitForSingleObject((HANDLE) mState, INFINITE);  
  5.     return dwWaitResult != WAIT_OBJECT_0 ? -1 : NO_ERROR;  
  6. }  
  7.   
  8. void Mutex::unlock()  
  9. {  
  10.     if (!ReleaseMutex((HANDLE) mState))  
  11.         LOG(LOG_WARN, "thread""WARNING: bad result from unlocking mutex\n");  
  12. }  
  13.   
  14. status_t Mutex::tryLock()  
  15. {  
  16.     DWORD dwWaitResult;  
  17.   
  18.     dwWaitResult = WaitForSingleObject((HANDLE) mState, 0);  
  19.     if (dwWaitResult != WAIT_OBJECT_0 && dwWaitResult != WAIT_TIMEOUT)  
  20.         LOG(LOG_WARN, "thread""WARNING: bad result from try-locking mutex\n");  
  21.     return (dwWaitResult == WAIT_OBJECT_0) ? 0 : -1;  
  22. }  

WaitForSingleObject函数的用法如下:

DWORD WaitForSingleObject(
HANDLE hObject, //指明一个内核对象的句柄
DWORD dwMilliseconds); //等待时间
  该函数需要传递一个内核对象句柄,该句柄标识一个内核对象,

如果该内核对象处于未通知状态,则该函数导致线程进入阻塞状态;

如果该内核对象处于已通知状态,则该函数立即返回WAIT_OBJECT_0。


第二个参数指明了需要等待的时间(毫秒),可以传递INFINITE指明要无限期等待下去,

如果第二个参数为0,那么函数就测试同步对象的状态并立即返回。

如果等待超时,该函数返回WAIT_TIMEOUT。

如果该函数失败,返回WAIT_FAILED。

<think> 我们正在讨论的是pthread_mutex_trylock返回错误码22(EINVAL)的问题。根据POSIX标准,错误码22表示无效参数(Invalid argument)。 结合引用[3]中提到的注意事项,以及引用[1][2]中关于互斥锁函数的行为,我们可以分析如下: 可能的原因: 1. 传递给pthread_mutex_trylock的互斥量指针无效(例如未初始化、已被销毁或指向非法地址)。 2. 互斥量已经被锁定,但当前线程已经持有该锁(非递归锁情况下尝试重复加锁)?注意:pthread_mutex_trylock在锁被占用时不会返回EINVAL,而是返回EBUSY(参考引用[1])。 3. 互斥量属性设置不正确,导致无法识别。 但是,根据引用[1]:pthread_mutex_trylock在锁被锁住时立即返回,但返回的是EBUSY(而不是EINVAL)。因此,锁被占用不会导致EINVAL。 根据引用[2]:pthread_mutex_timedlock在超时时返回ETIMEOUT,也不涉及EINVAL。 因此,我们聚焦于参数无效的情况。 解决方案: 1. 检查互斥量是否已经正确初始化(使用pthread_mutex_init或静态初始化PTHREAD_MUTEX_INITIALIZER)。 2. 确保传递给pthread_mutex_trylock的是一个有效的互斥量指针(非空且指向已初始化的互斥量)。 3. 检查是否有其他线程在操作该互斥量时将其销毁(如调用了pthread_mutex_destroy),然后又被尝试加锁。 4. 检查互斥量是否被意外修改(例如覆盖写)。 此外,错误码22也可能由以下情况导致: - 互斥量属性对象无效(如果在初始化时使用了属性对象,并且该属性对象未正确初始化或已被销毁)。 但是,注意pthread_mutex_trylock的参数只有一个互斥量,所以问题应该出在互斥量本身。 步骤: 1. 验证互斥量初始化:确保在第一次使用互斥量之前进行了初始化。 静态初始化:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 动态初始化:pthread_mutex_init(&mutex, &attr); // 注意attr需要有效或为NULL 2. 检查互斥量是否被销毁:确保在互斥量使用期间没有调用pthread_mutex_destroy。销毁后再使用会导致未定义行为。 3. 检查线程间同步:确保没有线程在另一个线程使用互斥量的同时销毁它。 4. 使用调试工具(如GDB、Valgrind)检查内存错误(例如互斥量被覆盖)。 示例代码(正确初始化): ```c pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 静态初始化 // 或者动态初始化 pthread_mutex_t mutex; if (pthread_mutex_init(&mutex, NULL) != 0) { // 处理初始化失败 } // 在某个线程中 int ret = pthread_mutex_trylock(&mutex); if (ret != 0) { if (ret == EBUSY) { // 锁已被占用 } else if (ret == EINVAL) { // 互斥量无效 // 需要检查初始化和销毁情况 } } ``` 如果问题仍然存在,可以尝试: - 使用动态初始化代替静态初始化,并在初始化时检查返回值。 - 在程序中对互斥量操作进行封装,确保每次使用前互斥量状态正确。 注意:在多线程程序中,互斥量的初始化和销毁应该由同一个线程管理,或者确保在初始化完成之后再创建其他线程,销毁时确保所有线程都已不再使用该互斥量。 相关问题: 1. 如何正确初始化和销毁pthread互斥量? 2. pthread_mutex_trylock返回EBUSY时应该如何处理? 3. 多线程程序中如何避免互斥量的无效使用? </think> ### `pthread_mutex_trylock` 返回错误码 22 (EINVAL) 的原因及解决方案 错误码 22 对应 `EINVAL`(无效参数),表示传递给函数的参数无效或互斥量状态异常。以下是详细分析和解决方案: --- #### 一、错误原因分析 1. **互斥量未初始化** - 原因:未调用 `pthread_mutex_init()` 或未使用 `PTHREAD_MUTEX_INITIALIZER` 初始化互斥量[^3]。 - 表现:对未初始化对象操作导致未定义行为。 2. **互斥量已被销毁** - 原因:调用 `pthread_mutex_destroy()` 销毁后再次尝试加锁。 - 表现:互斥量处于无效状态。 3. **无效属性设置** - 原因:初始化时指定的属性无效(如不支持的互斥类型)[^3]。 - 示例:尝试设置 `PTHREAD_MUTEX_RECURSIVE` 但底层实现不支持。 4. **传入无效指针** - 原因:传递了非法地址(如 NULL 指针)。 - 表现:`mutex` 参数指向无效内存区域。 --- #### 二、解决方案 ##### 1. 确保互斥量正确初始化 ```c // 静态初始化(推荐) pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 或动态初始化 pthread_mutex_t mutex; if (pthread_mutex_init(&mutex, NULL) != 0) { // 处理初始化失败 } ``` > ✅ **关键点**: > 静态初始化线程安全,优先使用 `PTHREAD_MUTEX_INITIALIZER` ##### 2. 检查互斥量生命周期 确保加锁操作在初始化后、销毁前: ```c void worker() { pthread_mutex_lock(&mutex); // ✅ 合法 // ... pthread_mutex_unlock(&mutex); } int main() { pthread_mutex_init(&mutex, NULL); // 创建工作线程... pthread_mutex_destroy(&mutex); // 销毁后不可再加锁 } ``` ##### 3. 验证互斥量属性 检查属性设置是否正确: ```c pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); // 设置错误检查类型 if (pthread_mutex_init(&mutex, &attr) != 0) { perror("属性设置失败"); // 返回EINVAL表示属性无效 } pthread_mutexattr_destroy(&attr); ``` ##### 4. 调试指针有效性 添加指针有效性检查: ```c int safe_mutex_trylock(pthread_mutex_t *mutex) { if (mutex == NULL) { errno = EINVAL; return -1; } return pthread_mutex_trylock(mutex); } ``` --- #### 三、调试流程 ```mermaid graph TD A[遇到EINVAL错误] --> B{检查初始化状态} B -->|未初始化| C[使用PTHREAD_MUTEX_INITIALIZER初始化] B -->|已初始化| D{检查销毁状态} D -->|已销毁| E[重新初始化或重建] D -->|未销毁| F{检查属性} F -->|属性无效| G[设置标准属性NULL] F -->|属性有效| H{检查指针} H -->|指针无效| I[修正指针传递] H -->|指针有效| J[使用Valgrind检测内存] ``` --- #### 四、预防措施 1. **统一初始化规范**:所有互斥量在声明时立即初始化。 2. **封装加锁函数**:封装带参数校验的加锁函数。 3. **使用静态分析工具**:通过 Clang Static Analyzer 检测无效参数。 4. **启用错误检查锁**:初始化时设置 `PTHREAD_MUTEX_ERRORCHECK` 属性。 --- #### 五、相关错误码对比 | 错误码 | 宏定义 | 原因 | |--------|--------------|-------------------------| | **22** | `EINVAL` | 无效参数或状态 | | 16 | `EBUSY` | 互斥量已被锁定[^1] | | 110 | `ETIMEDOUT` | 加锁超时(仅限timedlock)[^2] |
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值