为什么信号量使用errno而互斥锁直接返回错误?
首先来看 pthread 互斥锁相关函数的文档定义:
若调用成功,pthread_mutex_lock() 和 pthread_mutex_unlock() 函数应返回0;否则,应返回错误编号以指示错误。
这种设计确实合理,调用pthread_mutex_trylock时直接拿返回值就能判断是否成功锁,高效直观。但 semaphore 的sem_trywait却完全不同:失败时返回-1并设置errno,而且锁(信号量计数)被占用时返回的错误码是EAGAIN而非EBUSY,这种差异并非随意设计,背后有几个关键原因:
历史标准的迭代差异
pthread 系列属于较晚的 POSIX.1c 线程标准,设计时已经考虑到全局errno的线程安全问题(早期errno是全局变量,多线程环境下容易出问题),所以采用了“成功返回0,失败直接返回错误码”的模型,避免依赖全局变量。而 semaphore 相关函数源自更早的 POSIX.1 标准,当时的系统调用接口普遍遵循“成功返回有效值,失败返回-1并设置errno”的传统(比如open、read都是如此),sem_trywait自然继承了这套老接口风格。语义定位的本质区别
互斥锁(mutex)的核心是线程间互斥,pthread_mutex_trylock的语义就是“尝试抢占互斥锁”,失败的核心原因就是锁正被其他线程占用,用EBUSY(资源忙)来描述完全贴合语义。而信号量(semaphore)是用于同步场景(支持线程/进程间),sem_trywait的语义是“尝试获取一个信号量计数”,当计数为0时返回EAGAIN,是沿用了Unix系统中“资源暂时不可用,可稍后重试”的通用语义——这和非阻塞read无数据时返回EAGAIN的逻辑一致,是早期同步原语设计的语义延续。接口生态的一致性考量
pthread 作为一套独立的线程API体系,所有接口都严格遵循“0成功,非0错误码”的规则,这样开发者在使用 pthread 的创建、同步各类函数时,不需要切换错误检查的逻辑,降低了认知负担。而 semaphore 属于IPC(进程间通信)体系的一部分,和sem_open、sem_post等其他信号量函数保持一致的错误返回方式,也和整个IPC接口生态的风格统一。
内容的提问来源于stack exchange,提问作者doliphin

