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
我对 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