1、std::mutex基本介绍
1、引言
在C++多线程编程中,不同线程操作同一片数据空间时,如果没有进行进行线程访问的约束的话,那么就会出现,线程A在修改Data,同时线程B也在修改Data。那么最后的Data里面的数据是未知的,然而我们刚开始期望的是通过线程A操作完数据后的结果,线程B再接着操作的情况。所以我们需要在某一个线程操作该数据的时候,独占数据的所有权,其他线程不可获得和更改。
Mutex:互斥量,在C++11中,其头文件如下:
#include<thread>
#include<mutex>
按照Mutex的类型划分如下:
std::mutex | 基本的 Mutex 类 |
---|---|
std::recursive_mutex | 递归 Mutex 类 |
std::time_mutex | 定时 Mutex 类 |
std::recursive_mutex | 定时递归Mutex 类 |
接下来,我们看下C++11中关于Mutex的定义如下:
/// The standard mutex type.
class mutex : private __mutex_base
{
public:
typedef __native_type* native_handle_type;
#ifdef __GTHREAD_MUTEX_INIT
constexpr
#endif
mutex() noexcept = default;
~mutex() = default;
mutex(const mutex&) = delete; //不允许拷贝构造
mutex& operator=(const mutex&) = delete;//不允许move拷贝
void
lock()
{
int __e = __gthread_mutex_lock(&_M_mutex);
// EINVAL, EAGAIN, EBUSY, EINVAL, EDEADLK(may)
if (__e)
__throw_system_error(__e);
}
bool
try_lock() noexcept
{
// XXX EINVAL, EAGAIN, EBUSY
return !__gthread_mutex_trylock(&_M_mutex);
}
void
unlock()
{
// XXX EINVAL, EAGAIN, EPERM
__gthread_mutex_unlock(&_M_mutex);
}
native_handle_type
native_handle()
{ return &_M_mutex; }
};
void lock()
void unlock()
bool try_lock()
mutex调用lock时,调用的线程将锁住该互斥量。这时候存在有三种情况:
1、如果该互斥量被当前的线程进行锁住了,则会产生死锁。
2、如果当前mutex被其他线程锁住了,则当前线程则会阻塞在这里。
3、如果当前的mutex没有被锁住的情况,则将当前mutex进行锁住,直到调用unlock为止。
mutex调用try_lock时,表示当前线程尝试锁住该mutex,如果mutex被其他线程持有,则当前线程不会阻塞在这里,这时候也会出现三种情况:
1、如果该互斥量被其他线程锁住了,则当前调用线程就返回false,并且不会阻塞。
2、如果当前mutex被当前线程锁住了,则会产生死锁的情况。
3、如果当前的mutex没有被其他线程锁住,则当前线程就锁住了mutex,直到调用unlock为止。
mutex调用un_lock时,解锁,当前线程释放对mutex的持有。
一般来说,当Mutex配合操作资源时,和下面两个搭配使用。
std::lock_guard,与 Mutex RAII 相关,方便线程对互斥量上锁。
std::unique_lock,与 Mutex RAII 相关,方便线程对互斥量上锁,但提供了更好的上锁和解锁控制。
具体的例子如下:
std::mutex g_mutex;
void task()
{
for(int i = 0;i < 1000000;i++)
{
std::lock_guard<std::mutex> lock(g_mutex);
g_count++;
}
std::cout << g_count << std::endl;
}
//示例4
std::mutex g_mutex;
static std::list<int> g_listMsgQueue;
void pushMsgRecvQueue()
{
for (int i = 0;i < 10; i++)
{
std::unique_lock<std::mutex> lck(g_mutex,std::try_to_lock);
if(lck.owns_lock()) {
g_listMsgQueue.push_back(i);
}
}
}
2、std::mutex使用示例
std::mutex使用示例如下:
/*************************************************************************
> File Name: thread_mutex.cpp
> Author: 小和尚敲木鱼
> Mail:
> Created Time: Mon 20 Sep 2021 03:17:02 AM PDT
************************************************************************/
#include<iostream>
#include<thread>
#include<mutex>
using namespace std;
std::mutex g_mtx;
volatile int counter(0);
void function_1()
{
std::cout << __func__<< " begin:" << counter << std::endl;
for (int i = 0;i < 10;i++) {
if (g_mtx.try_lock()) {
counter = counter + 5;
g_mtx.unlock();
}
}
std::cout << __func__<< " end:" << counter << std::endl;
}
void function_2()
{
std::cout << __func__<< " begin:" << counter << std::endl;
for (int i = 0;i < 10;i++) {
if (g_mtx.try_lock()) {
counter = counter + 10;
g_mtx.unlock();
}
}
std::cout << __func__<< " end:" << counter << std::endl;
}
int main(int agc,char * agv[])
{
std::thread thread1(function_1);
std::thread thread2(function_2);
if (thread1.joinable())
thread1.join();
if (thread2.joinable())
thread2.join();
return 0;
}
//OUT
//function_2 begin:0
//function_2 end:100
//function_1 begin:100
//function_1 end:150
/**************************end of file**********************************/
3、std::recursive_mutex 递归互斥量介绍
/// The standard recursive mutex type.
class recursive_mutex : private __recursive_mutex_base
{
public:
typedef __native_type* native_handle_type;
recursive_mutex() = default;
~recursive_mutex() = default;
recursive_mutex(const recursive_mutex&) = delete;
recursive_mutex& operator=(const recursive_mutex&) = delete;
void
lock()
{
int __e = __gthread_recursive_mutex_lock(&_M_mutex);
// EINVAL, EAGAIN, EBUSY, EINVAL, EDEADLK(may)
if (__e)
__throw_system_error(__e);
}
bool
try_lock() noexcept
{
// XXX EINVAL, EAGAIN, EBUSY
return !__gthread_recursive_mutex_trylock(&_M_mutex);
}
void
unlock()
{
// XXX EINVAL, EAGAIN, EBUSY
__gthread_recursive_mutex_unlock(&_M_mutex);
}
native_handle_type
native_handle()
{ return &_M_mutex; }
};
由上面可知,std::recursive_mutex 支持的操作如下:
void lock()
void unlock()
bool try_lock()
其成员函数和std::mutex一样,其操作和mutex查不多。但是std::recursive_mutex 不同点在于,当前线程可以重复对互斥量进行上锁,即递归上锁,但是这个操作有一个前提就是,调用std::recursive_mutex 的lock和unlock次数得相同。当前线程持有std::recursive_mutex 时,可以重复调用std::recursive_mutex 不会阻塞。如果其他线程尝试持有std::recursive_mutex 的所有权,则会阻塞或者受到false的返回值。
4、std::time_mutex定时互斥量介绍
/// The standard timed mutex type.
class timed_mutex
: private __mutex_base, public __timed_mutex_impl<timed_mutex>
{
public:
typedef __native_type* native_handle_type;
timed_mutex() = default;
~timed_mutex() = default;
timed_mutex(const timed_mutex&) = delete;
timed_mutex& operator=(const timed_mutex&) = delete;
void
lock()
{
int __e = __gthread_mutex_lock(&_M_mutex);
// EINVAL, EAGAIN, EBUSY, EINVAL, EDEADLK(may)
if (__e)
__throw_system_error(__e);
}
bool
try_lock() noexcept
{
// XXX EINVAL, EAGAIN, EBUSY
return !__gthread_mutex_trylock(&_M_mutex);
}
template <class _Rep, class _Period>
bool
try_lock_for(const chrono::duration<_Rep, _Period>& __rtime)
{ return _M_try_lock_for(__rtime); }
template <class _Clock, class _Duration>
bool
try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime)
{ return _M_try_lock_until(__atime); }
void
unlock()
{
// XXX EINVAL, EAGAIN, EBUSY
__gthread_mutex_unlock(&_M_mutex);
}
native_handle_type
native_handle()
{ return &_M_mutex; }
};
由上面可知,std::time_mutex支持的操作如下:
void lock()
void unlock()
bool try_lock()
bool try_lock_for(const chrono::duration<_Rep, _Period>& __rtime) //新增
bool try_lock_until(try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime) //新增
由上面可知,std::time_mutex比std::mutex多了两个操作。try_lock_for(),try_lock_until()。
下面就分别对这两个操作进行解释:
std::time_mutex::try_lock_for 成员函数
接受参数,表示在这段时间范围之内,如果当前线程没有获得锁则阻塞住。
std::time_mutex::try_lock_until成员函数
成员函数表示,接受一个参数,在这个时间点之前线程没有获得锁就阻塞住,如果在这段时间内,其他线程释放了锁,则当前线程可以获得std::time_mutex的锁,如果超过了这个时间点,则返回false。
具体使用示例如下:
/*************************************************************************
> File Name: thread_time_mutex.cpp
> Author: 小和尚敲木鱼
> Mail:
> Created Time: Mon 20 Sep 2021 03:34:47 AM PDT
************************************************************************/
#include <iostream> // std::cout
#include <chrono> // std::chrono::milliseconds
#include <thread> // std::thread
#include <mutex> // std::timed_mutex
std::timed_mutex g_timerMtx;
using namespace std;
/*****************************文件说明***********************************
***********************************************************************/
/*
void function_1()
{
while(!g_timerMtx.try_lock_for(std::chrono::milliseconds(200)))
{
std::cout << "function do something" <<std::endl;
}
std::this_thread::sleep_for(std::chrono::milliseconds(200));
std::cout << "function exit..." << std::endl;
g_timerMtx.unlock();
}
int main(int agc,char * agv[])
{
std::thread threads[10];
for (int i = 0; i < 10; i++) {
threads[i] = std::thread(function_1);
}
for (auto & th : threads) {
if (th.joinable())
{
th.join();
}
}
return 0;
}
*/
//OUT
//function exit...
//function do something
//function do something
//function do something
//function do something
//function do something
//function do something
//function do something
//function do something
//function exit...
//function do something
//function do something
//function do something
//function do something
//function do something
//function do something
//function do something
//function exit...
//function do something
//function do something
//function do something
//function do something
//function do something
//function do something
//function do somethingfunction do something
//
//function do something
//function exit...
//function do something
//function do something
//function do something
//function exit...
//function do something
//function do something
//function do something
//function do something
//function exit...
//function do somethingfunction do something
//
//function do something
//function do something
//function exit...
//function do something
//function do something
//function do something
//function exit...
//function exit...
//function exit...
void function_1()
{
while(!(g_timerMtx.try_lock_until(std::chrono::milliseconds(200))))
{
std::cout << "function do something" <<std::endl;
}
std::this_thread::sleep_for(std::chrono::milliseconds(200));
std::cout << "function exit..." << std::endl;
g_timerMtx.unlock();
}
int main(int agc,char * agv[])
{
std::thread threads[10];
for (int i = 0; i < 10; i++) {
threads[i] = std::thread(function_1);
}
for (auto & th : threads) {
if (th.joinable())
{
th.join();
}
}
return 0;
}
/**************************end of file**********************************/
5、std::recursive_timed_mutex定时递归互斥量介绍
/// The standard recursive timed mutex type.
class recursive_timed_mutex
: private __recursive_mutex_base,
public __timed_mutex_impl<recursive_timed_mutex>
{
public:
typedef __native_type* native_handle_type;
recursive_timed_mutex() = default;
~recursive_timed_mutex() = default;
recursive_timed_mutex(const recursive_timed_mutex&) = delete;
recursive_timed_mutex& operator=(const recursive_timed_mutex&) = delete;
void
lock()
{
int __e = __gthread_recursive_mutex_lock(&_M_mutex);
// EINVAL, EAGAIN, EBUSY, EINVAL, EDEADLK(may)
if (__e)
__throw_system_error(__e);
}
bool
try_lock() noexcept
{
// XXX EINVAL, EAGAIN, EBUSY
return !__gthread_recursive_mutex_trylock(&_M_mutex);
}
template <class _Rep, class _Period>
bool
try_lock_for(const chrono::duration<_Rep, _Period>& __rtime)
{ return _M_try_lock_for(__rtime); }
template <class _Clock, class _Duration>
bool
try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime)
{ return _M_try_lock_until(__atime); }
void
unlock()
{
// XXX EINVAL, EAGAIN, EBUSY
__gthread_recursive_mutex_unlock(&_M_mutex);
}
native_handle_type
native_handle()
{ return &_M_mutex; }
};
可以看出std::recursive_timed_mutex和std::time_mutex的操作没有什么新增的函数,但是其具有std::recursive_mutex的特性,就是当前线程中,可以多次对std::recursive_timed_mutex进行上锁,不会阻塞线程本身。
void lock()
void unlock()
bool try_lock()
bool try_lock_for(const chrono::duration<_Rep, _Period>& __rtime)
bool try_lock_until(try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime)
6、总结
互斥量是C++11线程同步的基础,基本概念还是比较简单,配合lock_guard采用RALL的操作,进行资源操作时非常的方便。也非常的银杏。