lock_guard是否保护返回值

Does a lock_guard protect the return value?

本文关键字:保护 返回值 是否 guard lock      更新时间:2023-10-16

我有一个关于lock_guards和返回值的问题。我想用一些代码来说明我的问题:

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_;
};

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

这篇文章可以归结为一个问题:lock_guard是否保护我的退货价值

get2()足以保护返回值。

n_是按值返回的,因此会生成一个变量副本,以便将其与return n_语句一起返回给调用者。您的lock_guard将确保n_不会被更改,直到该副本返回给调用者,然后它被销毁,从而释放锁。

退回副本是正确的,但不会给您带来任何额外的好处。此外,在任何情况下,您都需要维护锁,直到副本也返回为止。因此,使用get1,除非编译器对其进行优化,否则您将为额外的副本分配付费,而不会获得太多收益。

两个版本是相等的。在销毁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