首页 > 解决方案 > 原子和 `errno`:使用 C++11 原子读取受保护的 errno 值是否合理?

问题描述

处理 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);
    };
}

这会导致任何类型的未定义行为或实现定义的行为吗?

标签: c++atomicerrno

解决方案


虽然 1988 年的旧 POSIX 标准曾经要求errno是一个全局对象,但在以后的修订版中不再是这种情况。至少 POSIX.1-2001 需要errno是线程本地的。我怀疑指定 POSIX 线程的 POSIX.1c-1995 已经要求这样做,但我无权访问该文档,因此无法验证。

我不希望支持 C++11 的 POSIX 系统也不会支持 POSIX 2001,因此,使用 C++11 原子似乎不太必要。

也就是说,虽然 C 标准不需要errno是全局对象(至少 C99 不需要),但它也不保证errno. errno因此,在非 POSIX 系统或不提供线程局部性的旧 POSIX 系统上可能需要锁定。如果由于某种原因平台确实支持 C++11,那么 C++11 原子可能是实现锁的理想选择 - 或者它可能不是。至少在理论上。

请注意,为了保证线程安全,您必须确保对可能设置的函数的所有调用都errno必须使用锁。如果您使用任何可能使用此类标准函数的库,则还必须锁定对此类函数的调用。


推荐阅读