【问题标题】:Does a lock_guard protect the return value? [duplicate]lock_guard 是否保护返回值? [复制]
【发布时间】:2020-06-05 19:56:19
【问题描述】:

我对@9​​87654322@s 和返回值有疑问。我想用一些代码来说明我的问题:

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 是否保护我的返回值?

【问题讨论】:

标签: c++ multithreading thread-safety mutex


【解决方案1】:

get2() 足以保护返回值。

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

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

【讨论】:

    【解决方案2】:

    两个版本是相同的。返回值是在std::lock_guard 被销毁之前创建的。

    你可以用下一个代码 sn-p 测试它

    #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
    

    【讨论】:

      猜你喜欢
      • 2011-04-20
      • 1970-01-01
      • 1970-01-01
      • 2014-11-24
      • 1970-01-01
      • 2017-10-01
      • 1970-01-01
      • 2014-06-14
      相关资源
      最近更新 更多