C++并发编程之线程同步 std::mutex用法总结

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的操作,进行资源操作时非常的方便。也非常的银杏。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

给大佬递杯卡布奇诺

你们的鼓励就是我传作的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值