如何 std::mutex::锁定直到函数返回

How to std::mutex::lock until function returns

本文关键字:函数 返回 锁定 std mutex 如何      更新时间:2023-10-16

我想返回一个std::vector .可以从其他线程(读取和写入(访问此std::vector。如何在函数返回完成后解锁std::mutex

例如,在以下情况下:

// Value.cpp
std::vector<int> GetValue()
{
  std::lock_guard<std::mutex> lock(mutex);
  // Do super smart stuff here
  // ...
  return m_value;
}
// MyThread.cpp
auto vec = myVec.GetValue();

现在,如果"在这里做超级聪明的事情"是空的怎么办:

// Value.cpp
std::vector<int> GetValue()
{
  std::lock_guard<std::mutex> lock(mutex);
  return m_value;
}
// MyThread.cpp
auto vec = myVec.GetValue();

那么锁仍然是强制性的吗?为什么?

使用std::lock_guard通过 RAII 处理锁定和解锁mutex,这就是它的确切目的。

int foo()
{
    std::lock_guard<std::mutex> lg(some_mutex);  // This now locked your mutex
    for (auto& element : some_vector)
    {
        // do vector stuff
    }
    return 5;
}  // lg falls out of scope, some_mutex gets unlocked

foo返回后,lg将超出范围,并在返回时unlock some_mutex

这是打印语句可以真正帮助解决的问题。 例如:

#include <mutex>
#include <iostream>
std::mutex mut;
template <class Mutex>
class Lock
{
    Mutex& mut_;
public:
    ~Lock()
    {
        std::cout << "unlockn";
        mut_.unlock();
    }
    Lock(const Lock&) = delete;
    Lock& operator=(const Lock&) = delete;
    Lock(Mutex& mut)
        : mut_(mut)
    {
        mut_.lock();
        std::cout << "lockn";
    }
};
struct A
{
    ~A()
    {
        std::cout << "~A() : " << this << "n";
    }
    A()
    {
        std::cout << "A() : " << this << "n";
    }
    A(const A& a)
    {
        std::cout << "A(const A&) : " << this << ", " << &a << "n";
    }
    A& operator=(const A& a)
    {
        std::cout << "A& operator=(const A&) : " << this << ", " << &a << "n";
        return *this;
    }
};
A a;
A
get()
{
    Lock<std::mutex> lk(mut);
    return a;
}
int
main()
{
    std::cout << "Startn";
    auto vec = get();
    std::cout << "Endn";
}

通过制作我自己的std::lock_guard版本,我可以插入打印语句来找出互斥锁何时被锁定和解锁。

通过制作一个假std::vector(上面称为A(,我可以将打印语句插入到我感兴趣的特殊成员中。 对我来说,这输出:

A() : 0x10fcfb188
Start
lock
A(const A&) : 0x7fff4ff06b28, 0x10fcfb188
unlock
End
~A() : 0x7fff4ff06b28
~A() : 0x10fcfb188

这清楚地表明,在复制0x10fcfb188处的A时,互斥锁被锁定。

可以通过以下方式更改测试以进行分配:

int
main()
{
    A vec;
    std::cout << "Startn";
    vec = get();
    std::cout << "Endn";
}

现在输出:

A() : 0x10d8a7190
A() : 0x7fff5235ab28
Start
lock
A(const A&) : 0x7fff5235ab18, 0x10d8a7190
unlock
A& operator=(const A&) : 0x7fff5235ab28, 0x7fff5235ab18
~A() : 0x7fff5235ab18
End
~A() : 0x7fff5235ab28
~A() : 0x10d8a7190

乍一看,分配似乎在锁外进行,因此看起来不安全。 然而,经过仔细检查,人们会看到0x10d8a7190处的受保护A被复制到锁内的临时A。 然后解锁互斥锁,并从临时互斥锁到本地进行分配。 没有其他线程可以引用临时线程。 所以只要没有其他线程引用vec,这又是安全的。