lock_guard 是否保护 return 值?

Does a lock_guard protect the return value?

我对 lock_guard 和 return 值有疑问。我想用一些代码来说明我的问题:

class Semaphore
{
public:
    Semaphore() = delete;
    Semaphore(int n);

    /**
     * Increases semaphore by one.
     */
    void up()
    {
        std::lock_guard<std::mutex> lg(m_);
        ++n_;
    }

    /**
     * Decreases semaphore by one.
     */
    void down()
    {
        std::lock_guard<std::mutex> lg(m_);
        --n_;
    }

    /**
     * Returns the underlying value of the semaphore.
     */
    int get1() const
    {
        std::lock_guard<std::mutex> lg(m_);
        int tmp = n_;
        return tmp;
    }

    /**
     * Returns the underlying value of the semaphore.
     */
    int get2() const
    {
        std::lock_guard<std::mutex> lg(m_);
        return n_;
    }

private:
    mutable std::mutex m_;
    int n_;
};

上面的class是一个Semaphore的简单实现。哪个 get 方法是线程安全的? get2 足够好还是我必须使用 get1?我是否必须将内部值 n_ 复制到临时变量,或者我可以立即 return 吗?

这个 post 归结为问题:lock_guard 是否保护我的 return 值?

get2()足以保护return值。

n_ 是按值 return 编辑的,因此使用 return n_ 语句将变量的副本 return 发送给调用者。您的 lock_guard 将确保 n_ 不会被更改,直到该副本被 returned 给调用者,之后它被销毁从而释放锁。

退回副本并没有错,但不会给您带来任何额外好处。此外,在任何情况下,您都需要保持锁定,直到副本也被 returned。因此,使用 get1 除非编译器对其进行优化,否则您需要支付额外的复制分配费用而不会获得太多收益。

两个版本相同。 Return 值在 std::lock_guard 被销毁之前创建。

您可以使用下一个代码片段对此进行测试

#include <iostream>
#include <memory>
#include <mutex>

template <typename T>
struct GuardWrapper : std::lock_guard<T> {
    GuardWrapper(T &e) : std::lock_guard<T>(e) { std::cout << "Creating guard" << std::endl; }
    ~GuardWrapper() { std::cout << "Deleting guard" << std::endl; }
};

struct A {
    A() { std::cout << "Creating A" << std::endl; }
    A(const A& other) {
        *this = other;
        std::cout << "Copying A" << std::endl;
    }
    ~A() { std::cout << "Deleting A" << std:: endl; }
};

struct B {
    A test() {
        GuardWrapper<std::mutex> guard(m_);
        return a_;
    }
    A test_with_tmp() {
        GuardWrapper<std::mutex> guard(m_);
        A tmp = a_;
        return tmp;
    }

   private:
    std::mutex m_;
    A a_;
};

int main () {
    std::cout << "init" << std::endl;
    B b;
    std::cout << "try without temp" << std::endl;
    A a0 = b.test();
    std::cout << "try with temp" << std::endl;
    A a1 = b.test_with_tmp();
    std::cout << "cleanup" << std::endl;
}

输出是

init
Creating A
try without temp
Creating guard
Copying A
Deleting guard
try with temp
Creating guard
Copying A
Deleting guard
cleanup
Deleting A
Deleting A
Deleting A