C++2.0 —— 多线程的使用之std::mutex

std::mutex 是C++11 中最基本的互斥量,std::mutex 对象提供了独占所有权的特性——即不支持递归地对 std::mutex 对象上锁,而 std::recursive_lock 则可以递归地对互斥量对象上锁。

Mutex 分类

  • std::mutex,最基本的 Mutex 类。
  • std::recursive_mutex,递归 Mutex 类。
  • std::time_mutex,定时 Mutex 类。
  • std::recursive_timed_mutex,定时递归 Mutex 类。

通用锁管理的分类

  • std::lock_guard,与 Mutex RAII 相关,方便线程对互斥量上锁。
  • std::unique_lock,与 Mutex RAII 相关,方便线程对互斥量上锁,但提供了更好的上锁和解锁控制。

Lock参数

  • std::once_flag
  • std::adopt_lock_t
  • std::defer_lock_t
  • std::try_to_lock_t

函数

  • std::try_lock,尝试同时对多个互斥量上锁。
  • std::lock,可以同时对多个互斥量上锁。
  • std::call_once,如果多个线程需要同时调用某个函数,call_once 可以保证多个线程对该函数只调用一次。

互斥

std::mutex

mutex 类是能用于保护共享数据免受从多个线程同时访问的同步原语。mutex 提供排他性非递归所有权语义:

  • 调用方线程从它成功调用 lock 或 try_lock 开始,到它调用 unlock 为止占有 mutex 。
  • 线程占有 mutex 时,所有其他线程若试图要求 mutex 的所有权,则将阻塞(对于 lock 的调用)或收到 false 返回值(对于 try_lock ).
  • 调用方线程在调用 lock 或 try_lock 前必须不占有 mutex 。

若 mutex 在仍为任何线程所占时被销毁,或在占有 mutex 时线程终止,则行为未定义。 mutex 类满足互斥体 (Mutex) 标准布局类型 (StandardLayoutType) 的全部要求;std::mutex 既不可复制亦不可移动

 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;

    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; }
  };

成员函数

(构造函数)

构造互斥
(公开成员函数)

(析构函数)

销毁互斥
(公开成员函数)

operator=

[被删除]

不可复制赋值
(公开成员函数)

锁定

lock

锁定互斥,若互斥不可用则阻塞
(公开成员函数)

try_lock

尝试锁定互斥,若互斥不可用则返回
(公开成员函数)

unlock

解锁互斥
(公开成员函数)

原生句柄

native_handle

返回底层实现定义的原生句柄
(公开成员函数)
  • 构造函数,std::mutex不允许拷贝构造,也不允许 move 拷贝,最初产生的 mutex 对象是处于 unlocked 状态的。
  • lock(),调用线程将锁住该互斥量。线程调用该函数会发生下面 3 种情况:(1). 如果该互斥量当前没有被锁住,则调用线程将该互斥量锁住,直到调用 unlock之前,该线程一直拥有该锁。(2). 如果当前互斥量被其他线程锁住,则当前的调用线程被阻塞住。(3). 如果当前互斥量被当前调用线程锁住,则会产生死锁(deadlock)。
  • unlock(), 解锁,释放对互斥量的所有权。
  • try_lock(),尝试锁住互斥量,如果互斥量被其他线程占有,则当前线程也不会被阻塞。线程调用该函数也会出现下面 3 种情况,(1). 如果当前互斥量没有被其他线程占有,则该线程锁住互斥量,直到该线程调用 unlock 释放互斥量。(2). 如果当前互斥量被其他线程锁住,则当前调用线程返回 false,而并不会被阻塞掉。(3). 如果当前互斥量被当前调用线程锁住,则会产生死锁(deadlock)。

栗子:

#include <iostream>
#include <thread>
#include <mutex>

using namespace std;


class A 
{
public:
	A() :m_count(0){}
	void myprint()
	{
		//chrono::milliseconds s(200);
		//this_thread::sleep_for(s);

		for (int i = 0; i < 1000000; i++)
		{
			m_mutex.lock();
			m_count++;
			m_mutex.unlock();
		}

		cout << "myPrint thread_id: " << this_thread::get_id() << " m_count: " << m_count << endl;
	}

	void myPrint2()
	{
		int unlock_count = 0;
		for (int i = 0; i < 1000000; i++)
		{
			//m_mutex.lock();

			if (m_mutex.try_lock()) 
			{
				m_count++;
				m_mutex.unlock();
			}
			else 
			{
				unlock_count++;
				cout << "myPrint2 thread_id: " << this_thread::get_id() << " unlock_count: " << unlock_count << endl;
			}


		}
	}

public:
	int m_count;
	std::mutex m_mutex;
};


int main()
{
	cout << " this is main thread id: " << this_thread::get_id() << endl;

	A a1;

	thread t1(&A::myprint,&a1);
	//t1.detach();

	thread t2(&A::myPrint2, &a1);

	t1.join();
	t2.join();

	cout << "main thread end" << " a1.m_count:  "<< a1.m_count <<  endl;

	return system("pause");
}

std::recursive_mutex

recursive_mutex 类是同步原语,能用于保护共享数据免受从个多线程同时访问;recursive_mutex 提供排他性递归所有权语义:

  • 调用方线程在从它成功调用 lock 或 try_lock 开始的时期里占有 recursive_mutex 。此时期间,线程可以进行对 lock 或 try_lock 的附加调用。所有权的时期在线程调用 unlock 匹配次数时结束。
  • 线程占有 recursive_mutex 时,若其他所有线程试图要求 recursive_mutex 的所有权,则它们将阻塞(对于调用 lock )或收到 false 返回值(对于调用 try_lock )。
  • 可锁定 recursive_mutex 次数的最大值是未指定的,但抵达该数后,对 lock 的调用将抛出 std::system_error 而对 try_lock 的调用将返回 false 。

若 recursive_mutex 在仍为某线程占有时被销毁,则程序行为未定义。 recursive_mutex 类满足互斥体 (Mutex) 标准布局类型 (StandardLayoutType) 的所有要求。

成员函数

(构造函数)

构造互斥
(公开成员函数)

(析构函数)

销毁互斥
(公开成员函数)

operator=

[被删除]

不可复制赋值
(公开成员函数)

锁定

lock

锁定互斥,若互斥不可用则阻塞
(公开成员函数)

try_lock

尝试锁定互斥,若互斥不可用则返回
(公开成员函数)

unlock

解锁互斥
(公开成员函数)

与std::mutex 不同的是:std::recursive_mutex 允许同一个线程对互斥量多次上锁(即递归上锁),来获得对互斥量对象的多层所有权,std::recursive_mutex 释放互斥量时需要调用与该锁层次深度相同次数的 unlock(),可理解为 lock() 次数和 unlock() 次数相同。

std::time_mutex

以类似 mutex 的行为, timed_mutex 提供排他性非递归所有权语义,timed_mutex 提供通过 try_lock_for() 和 try_lock_until() 方法试图带时限地要求 timed_mutex 所有权的能力。

成员函数

(构造函数)

构造互斥
(公开成员函数)

(析构函数)

销毁互斥
(公开成员函数)

operator=

[被删除]

不可复制赋值
(公开成员函数)

锁定

lock

锁定互斥,若互斥不可用则阻塞
(公开成员函数)

try_lock

尝试锁定互斥,若互斥不可用则返回
(公开成员函数)

try_lock_for

尝试锁定互斥,若互斥在指定的时限时期中不可用则返回
(公开成员函数)

try_lock_until

尝试锁定互斥,若直至抵达指定时间点互斥不可用则返回
(公开成员函数)

unlock

解锁互斥
(公开成员函数)

try_lock_for: 函数接受一个时间范围,表示在这一段时间范围之内线程如果没有获得锁则被阻塞住(与 std::mutex 的 try_lock() 不同,try_lock 如果被调用时没有获得锁则直接返回 false),如果在此期间其他线程释放了锁,则该线程可以获得对互斥量的锁,如果超时(即在指定时间内还是没有获得锁),则返回 false。

try_lock_until: 函数则接受一个时间点作为参数,在指定时间点未到来之前线程如果没有获得锁则被阻塞住,如果在此期间其他线程释放了锁,则该线程可以获得对互斥量的锁,如果超时(即在指定时间内还是没有获得锁),则返回 false。

std::recursive_timed_mutex

以类似 std::recursive_mutex 的方式, recursive_timed_mutex 提供排他性递归所有权语义。另外, recursive_timed_mutex 通过 try_lock_for 与 try_lock_until 方法,提供带时限地试图要求 recursive_timed_mutex 所有权的能力。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值