概述
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
所有权的能力。
最后
以上就是发嗲小蝴蝶为你收集整理的C++2.0 —— 多线程的使用之std::mutex互斥 的全部内容,希望文章能够帮你解决C++2.0 —— 多线程的使用之std::mutex互斥 所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复