mutex
# mutex
namespace std
{
//mutex系列类
class mutex;//基本的mutex类
class recursive_mutex;//递归 mutex 类。
class timed_mutex;//定时 mutex 类。
class recursive_timed_mutex;//定时递归 mutex 类。
//其他类型
struct adopt_lock_t;
struct defer_lock_t;
struct try_to_lock_t;
struct once_flag;
constexpr adopt_lock_t adopt_lock{};
constexpr defer_lock_t defer_lock{};
constexpr try_to_lock_t try_to_lock{};
//lock类
template<typename LockableType>
class lock_guard;
template<typename LockableType>
class unique_lock;
//函数
template<typename LockableType1,typename... LockableType2>
void lock(LockableType1& m1,LockableType2& m2...);//同时对多个互斥量上锁。
template<typename LockableType1,typename... LockableType2>
int try_lock(LockableType1& m1,LockableType2& m2...);//尝试同时对多个互斥量上锁。
template<typename Callable,typename... Args>
void call_once(once_flag& flag,Callable func,Args args...);//如果多个线程需要同时调用某个函数,call_once 可以保证多个线程对该函数只调用一次。
}
# std::mutex类
class mutex
{
public:
mutex(mutex const&)=delete;
mutex& operator=(mutex const&)=delete;
constexpr mutex() noexcept;
~mutex();
void lock();
void unlock();
bool try_lock();
};
#include <iostream> //std::cout
#include <thread> //std::thread
#include <mutex> //std::mutex
#include <chrono> //std::chrono
volatile int counter(0); // non-atomic counter
std::mutex mtx; // locks access to counter
void attempt_10k_increases()
{
for (int i=0; i<100; ++i)
{
if (mtx.try_lock())
{ // only increase if currently not locked:
++counter;
std::this_thread::sleep_for(std::chrono::microseconds(50));
mtx.unlock();
}
else
{
std::cout << std::this_thread::get_id() << " failed" << std::endl;
}
}
}
int main (int argc, const char* argv[])
{
std::thread threads[10];
for (int i=0; i<10; ++i)
{
threads[i] = std::thread(attempt_10k_increases);
}
for (auto& th : threads)
{
th.join();
}
std::cout << counter << " successful increases of the counter.\n";
return 0;
}
# std::recursive_mutex类
std::recursive_mutex 允许同一个线程对互斥量多次上锁(即递归上锁),来获得对互斥量对象的多层所有权,std::recursive_mutex 释放互斥量时需要调用与该锁层次深度相同次数的 unlock(),可理解为 lock() 次数和 unlock() 次数相同,除此之外,std::recursive_mutex 的特性和 std::mutex 大致相同。
# std::timed_mutex类
class timed_mutex
{
public:
timed_mutex(timed_mutex const&)=delete;
timed_mutex& operator=(timed_mutex const&)=delete;
timed_mutex();
~timed_mutex();
void lock();
void unlock();
bool try_lock();
//在指定的relative_time时间内,尝试获取锁。
template<typename Rep,typename Period>
bool try_lock_for(std::chrono::duration<Rep,Period> const& relative_time);
//时间点 chrono::system_clock.now()+timeout
template<typename Clock,typename Duration>
bool try_lock_until(std::chrono::time_point<Clock,Duration> const& absolute_time);
};
# std::lock_guard类型模板
template <class Mutex>
class lock_guard
{
public:
typedef Mutex mutex_type;
explicit lock_guard(mutex_type& m);
lock_guard(mutex_type& m, adopt_lock_t);
~lock_guard();
lock_guard(lock_guard const& ) = delete;
lock_guard& operator=(lock_guard const& ) = delete;
};
指定的互斥量在构造函数中上锁,在析构函数中解锁。这就为互斥量锁部分代码提供了一个简单的方式;当程序运行完成时,阻塞解除,互斥量解锁(无论是执行到最后,还是通过控制流语句break或return,亦或是抛出异常)。
std::mutex g_mtx;
void threadfunc(char ch)
{
//首先,这在一个局部作用域内, std::lock_guard 在构造时,
//会调用 g_mutex->lock() 方法;
std::lock_guard<std::mutex> lock(g_mtx);
for (int i = 0; i < 5; ++i)
{
//std::lock_guard<std::mutex> lock(g_mtx); //加锁
for (int j = 0; j < 5; ++j)
{ // std::lock_guard<std::mutex> lock(g_mtx);
printf("i=%d,j=%d %c", i,j,ch);
}
printf("\n");
}
printf("\n");
//局部作用域代码结束后,
//std::lock_guard 的析构函数会被调用,
//函数中会调用 g_mutex->unlock() 方法。
}
int main()
{
char ch = 'a';
std::thread that[5];
for (int i = 0; i < 5; ++i)
{
that[i] = std::thread(threadfunc, ch + i);
}
for (int i = 0; i < 5; ++i)
{
that[i].join();
}
cout << "Main End" << endl;
return 0;
}
# std::unique_lock类型模板
临时加解锁
void shared_print(string msg, int id) {
std::unique_lock<std::mutex> guard(_mu, std::defer_lock);//延时加锁
//do something 1
guard.lock();
// do something protected
guard.unlock(); //临时解锁
//do something 2
guard.lock(); //继续上锁
// do something 3
f << msg << id << endl;
cout << msg << id << endl;
// 结束时析构guard会临时解锁
}
通过std::defer_lock设置初始化的时候不进行默认的上锁操作。在无需加锁的操作时,可以先临时释放锁,然后需要继续保护的时候,可以继续上锁,这样就无需重复的实例化lock_guard对象,还能减少锁的区域。
# std::lock函数模板
std::lock函数模板提供同时锁住多个互斥量的功能,且不会有因改变锁的一致性而导致的死锁。
# std::try_lock函数模板
std::try_lock函数模板允许尝试获取一组可锁对象上的锁,所以要不全部获取,要不一个都不获取。
# 练习
c++多线程中死锁多出现在使用多个mutex,而mutex的顺序不一样。比如下面的代码为了保证输出的原子性,在两个输出的函数中使用了两个mutex,但是这两个mutex锁的顺序是不一样的,结果就造成了死锁。
#include<iostream>
#include<thread>
#include<mutex>
#include<string>
#include<fstream>
class LofFile
{
public:
void share_print(std::string msg, int id) {
std::lock_guard<std::mutex> guard(m_mutex);
std::lock_guard<std::mutex> guard2(m_mutex2);
std::cout << msg << " " << id << std::endl;
}
void share_print_f(std::string msg, int id) {
std::lock_guard<std::mutex> guard(m_mutex2);
std::lock_guard<std::mutex> guard2(m_mutex);
std::cout << msg << " " << id << std::endl;
}
private:
std::mutex m_mutex;
std::mutex m_mutex2;
};
void fun1(LofFile& log)
{
for (int i = 0; i < 50; i++)
{
log.share_print("fun1 id", i);
}
}
int main(int argc, char** argv)
{
LofFile log;
std::thread t1(fun1, std::ref(log));
for (int i = 0; i < 50; i++)
{
log.share_print_f("main id", i);
}
if (t1.joinable()) {
t1.join();
}
return 0;
}
如何避免在C++中使用mutex出现死锁 1、判断自己的代码是否需要多个mutex 2、清楚了解自己调用的方法之类的是否也有使用mutex的情况 3、使用标准库中的std::Lock(std::mutex,···)来设置mutex
如何使用std::Lock(std::mutex,···)
class LofFile
{
public:
void share_print(std::string msg, int id) {
std::lock(m_mutex, m_mutex2);
std::lock_guard<std::mutex> guard(m_mutex,std::adopt_lock);
std::lock_guard<std::mutex> guard2(m_mutex2,std::adopt_lock);
std::cout << msg << " " << id << std::endl;
}
void share_print_f(std::string msg, int id) {
std::lock(m_mutex, m_mutex2);
std::lock_guard<std::mutex> guard(m_mutex2,std::adopt_lock);
std::lock_guard<std::mutex> guard2(m_mutex,std::adopt_lock);
std::cout << msg << " " << id << std::endl;
}
private:
std::mutex m_mutex;
std::mutex m_mutex2;
};
主要修改了两个地方 调用std::lock_guardstd::mutex 前先使用std::lock(std::mutex,···), 其次在std::lock_guardstd::mutex 中加入std::adopt_lock参数