处理errno是在多线程环境中使用POSIX API的努力之一。像下面这样使用std::atomic
进行锁定是否合理?
class FastLock{
std::atomic_int value;
public:
FastLock()
: value{0}{}
void unlock()
{
value.store(0,std::memory_order_release);
}
bool try_lock()
{
int r = value.exchange(1,std::memory_order_acquire);
return !r;
}
};
上下文类似于:
template<typename function, typename ...args>
auto shield(function _fn){
static FastLock* lk = new FastLock{};
return [=](args... _v){
while(lk->try_lock());
auto ret = std::forward(_fn, _v...);
auto errval = errno;
lk->unlock();
return std::make_pair(ret,errval);
};
}
这会导致未定义的行为或实现定义的行为吗?
答案 0 :(得分:3)
尽管1988年的旧POSIX标准曾经要求errno
为全局对象,但在以后的版本中不再是这种情况。至少POSIX.1-2001要求errno
是线程本地的。我怀疑指定POSIX线程的POSIX.1c-1995已经要求这样做,但是我无权访问该文档,因此无法验证。
我不希望支持C ++ 11的POSIX系统也不会支持POSIX 2001,因此,似乎不太可能需要使用C ++ 11原子。
也就是说,尽管C标准不需要errno
作为全局对象(至少C99不需要),但它也不保证errno
的线程局部性。因此,在非POSIX系统或不提供线程局部性的旧POSIX系统上,可能需要锁定errno
。并且,如果由于某种原因该平台确实支持C ++ 11,则C ++ 11原子可能是实现锁的理想选择-也可能不是。至少在理论上。
请注意,为了保证线程安全,必须确保对可能设置errno
的函数的所有调用都必须使用锁。如果使用任何可能使用此类标准功能的库,则也必须锁定对此类功能的调用。