std::mutex
mutex就是互斥量的意思,在c++中使用互斥量需要包含#include
引入互斥量
之前了解了线程访问公有数据是不安全的,所以使用互斥量来防治线程不安全的操作。
互斥量就是一个变量,只有两种状态,加锁和解锁。每一个互斥量管理一个公有数据,一个线程访问公有数据后,互斥量加锁,其他线程就不能访问,等待之前的线程访问完成解锁后,才能访问。
mutex分类
Mutex 系列类(四种)
std::mutex,基本Mutex 类。
std::recursive_mutex,递归 Mutex 类。
std::time_mutex,定时基本Mutex 类。
std::recursive_timed_mutex,定时递归 Mutex 类。
std::mutex
构造:不允许拷贝构造和move构造,初始化为解锁状态。
成员函数:
lock():加锁,如果已经被其他线程加锁了,则会阻塞
unlock():解锁,在线程没有获得锁是不能进行解锁
try_lock():尝试加锁,如果已经被其他线程加锁了,则不会阻塞,返回false。
mutex mu;
int x{ 0 };
mu.lock();
x++;
mu.unlock();
std::recursive_mutex
std::mutex只能进行一次上锁解锁操作,std::recursive_mutex允许一个线程对一个互斥量多次上锁,但解锁次数也必须相同。
recursive_mutex mu;
int x{ 0 };
mu.lock();mu.lock();
x++;
mu.unlock();mu.unlock();
std::timed_mutex
try_lock_for():
新增的成员函数,尝试一直阻塞一个时间段加锁,如果超过这个时间段没有获得锁则返回false
try_lock_until():
新增的成员函数,尝试在一个时间点前加锁,如果这个时间段没有获得锁则会返回false
recursive_mutex mu;
int x{ 0 };
if (mu.try_lock_for(std::chrono::microseconds(10))) //尝试阻塞10秒加锁
{
++counter;
mu.unlock(); //解锁
}
自动加锁
上面的都是手动进行加锁和解锁,但有时如果人们加锁后忘记了了解锁,就会造成死锁,所以c++11封装了上面的加锁解锁操作,制作了std::lock_guard 与 std::unique_lock来进行自动的加锁和解锁操作。
std::lock_guard
std::lock_guard 在构造函数中进行加锁,析构函数中进行解锁。所以我们可以在栈上创建std::lock_guard,在生命周期结束后,自动解锁。
int x {0 }; //原子变量
mutex mu; //互斥量
void fun()
{
std::lock_guard<std::mutex> lock(mu);
this_thread::sleep_for(std::chrono::microseconds(100));
x++;
}
int main()
{
for (size_t i = 0; i < 100; i++)
{
std::thread(fun).detach();
}
this_thread::sleep_for(std::chrono::microseconds(5000));
cout << x << endl;
system("pause");
return 0;
}
std::unique_lock
std::unique_lock比std::lock_guard功能更加强大,但是会耗费更多的时间和空间。