I want to return a std::vector
. This std::vector
may be accessed from other threads (read and write). How can I unlock my std::mutex
just
This is the sort of question that print statements can really help with. For example:
#include <mutex>
#include <iostream>
std::mutex mut;
template <class Mutex>
class Lock
{
Mutex& mut_;
public:
~Lock()
{
std::cout << "unlock\n";
mut_.unlock();
}
Lock(const Lock&) = delete;
Lock& operator=(const Lock&) = delete;
Lock(Mutex& mut)
: mut_(mut)
{
mut_.lock();
std::cout << "lock\n";
}
};
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 << "Start\n";
auto vec = get();
std::cout << "End\n";
}
By making my own version of std::lock_guard
, I can insert print statements to find out when the mutex gets locked and unlocked.
And by making a fake std::vector
(called A
above), I can insert print statements into the special members that I'm interested in. For me this outputs:
A() : 0x10fcfb188
Start
lock
A(const A&) : 0x7fff4ff06b28, 0x10fcfb188
unlock
End
~A() : 0x7fff4ff06b28
~A() : 0x10fcfb188
which clearly shows that the mutex is locked while the A
at 0x10fcfb188 is being copied from.
The test can be altered for assignment with:
int
main()
{
A vec;
std::cout << "Start\n";
vec = get();
std::cout << "End\n";
}
which now outputs:
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
At first it looks like the assignment takes place outside the lock, and thus looks unsafe. However upon closer inspection one sees that the protected A
at 0x10d8a7190 is copied to a temporary A
inside the lock. The mutex is then unlocked and an assignment is made from the temporary to the local. No other thread could possibly reference the temporary. So as long as no other thread references vec
, this is again safe.
Use a std::lock_guard to handle locking and unlocking the mutex
via RAII, that is literally what it was made for.
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
After foo
returns, lg
will fall out of scope, and unlock
some_mutex
when it does.