谈一谈多线程编程中的涉及到的那些锁机制--mutex、lock_guard、unique_lock、shared_lock、scoped_lock

 谈到多线程编程,我们必须了解一下C++中提供的一些保证数据的同步和互斥的锁机制。

必谈的RAII原则

RAII原则是所有的资源都必须有管理对象,而资源的申请操作在管理对象的构造函数中进行,而资源的回收则在管理对象的析构函数中进行。

先来讲讲std::mutex

std::mutex是C++标准库中提供的最基本的互斥锁之一。它用于实现线程间的互斥访问,即在一个时间点只允许一个线程获得锁,其他线程需要等待锁被释放才能继续执行。使用std::mutex可以保证多个线程对共享资源的访问顺序,并避免数据竞争产生的问题。
 在应用过程中,每个线程在对资源操作前都尝试先加锁,成功加锁才能操作,操作结束解锁。通过“锁”就将资源的访问变成互斥操作,之后与时间有关的错误也不会再产生。但是同一时刻,只能有一个线程持有该锁。当A线程对某个全局变量加锁访问,B在访问前尝试加锁,拿不到锁,B阻塞。C线程不加锁,直接访问该全局变量,依然能够访问,但会出现数据混乱。所以,互斥锁实质上是操作系统提供的一把“建议锁”(又称“协同锁”),建议程序中有多线程访问共享资源的时候使用该机制。但并没有强制限定。
主要应用函数:(成功返回0, 失败返回错误号)

  • 初始化一个互斥锁,参数1为传出参数(调用时应传 &mutex),参数二是互斥量属性。是一个传入参数,通常传NULL,选用默认属性(线程间共享)。
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);</pre>
<pre>pthread_mutex_destroy()----销毁一个互斥锁            
pthread_mutex_lock()----加锁             
pthread_mutex_trylock()----尝试加锁          
pthread_mutex_unlock()----解锁

注意:互斥锁初始化有两种方式:

  1. 静态初始化:如果互斥锁 mutex 是静态分配的(定义在全局,或加了static关键字修饰),可以直接使用宏进行初始化。例如: pthead_mutex_t muetx = PTHREAD_MUTEX_INITIALIZER;
  2. 动态初始化:局部变量应采用动态初始化。例如: pthread_mutex_init(&mutex, NULL)

加锁与解锁:

  • lock与unlock以及trylock:lock尝试加锁,如果加锁不成功,线程阻塞,阻塞到持有该互斥量的其他线程解锁为止。unlock主动解锁函数,同时将阻塞在该锁上的所有线程全部唤醒,至于哪个线程先被唤醒,取决于优先级、调度。默认:先阻塞、先唤醒。但是,trylock加锁失败直接返回错误号(如:EBUSY),不阻塞。

需要注意的是:
 std::mutex.lock是我们在C++中比较常见的锁,我们使用std::mutex.lock方法时,同时需要考虑何时使用std:mutex.unlock方法去解锁。如果在复杂的多线程情况下,加锁、解锁的时机很难把握,也不好实现。所以C++在新标准里提供了lock_guard, unique_lock, shared_lock, 和 scoped_lock四种锁,用于各种复杂情况。这四种锁都是满足RAII风格。

lock_guard

lock_guard用于在作用域内自动管理互斥量的锁定和解锁。当 lock_guard 对象被创建时,它会自动锁定互斥量,当对象离开作用域时,它会自动解锁互斥量。lock_guard 不支持手动锁定和解锁,也不支持条件变量,基本用法如下:
std::lock_guard<std::mutex> lock(mtx);

unique_lock

它提供了比std::lock_guard更多的功能和灵活性,但是在不需要额外灵活性时,使用后者可能是一个更高效的选择。

  • 默认构造
std::mutex mtx;
std::unique_lock<std::mutex> lock(mtx);
若仅传递一个互斥量给 std::unique_lock 的构造函数,默认会立即尝试上锁
  • 延迟锁定
std::mutex mtx;
std::unique_lock<std::mutex> lock(mtx, std::defer_lock);
// 此时 mtx 没有被锁定,可以稍后通过调用 lock.lock() 来手动上锁
  • 尝试锁定-创建 std::unique_lock 对象时尝试锁定互斥量,如果互斥量已被其他线程锁定,也不会阻塞当前线程。
std::unique_lock<std::mutex> lock(mtx, std::try_to_lock);
  • adopt_lock --如果互斥量已经被手动锁定,我们可以使用 std::unique_lock 并传递 std::adopt_lock 标记来接管互斥量的所有权,这告诉 std::unique_lock 对象互斥量已经被锁定。
std::mutex mtx;
mtx.lock();//--切记手动锁定
std::unique_lock<std::mutex> lock(mtx, std::adopt_lock);
// 此时 mtx 被认为已经被 lock 锁定
  • 释放锁 unlock()
     如果 std::unique_lock 对象当前持有锁(即拥有锁的标记为 true),则调用此方法会释放互斥量,并将拥有锁的标记设置为 false。如果对象没有持有锁,则调用 unlock() 会导致程序行为未定义。

shared_lock

shared_lock:用于共享互斥量(std::shared_mutex 或 std::shared_timed_mutex)的锁,允许多个线程同时读取共享数据,但在写入数据时仍然保证互斥。当对象离开作用域时,它会自动解锁共享互斥量。但是其支持手动锁定和解锁,以及尝试锁定。

scoped_lock

scoped_lock用于同时锁定多个互斥量,以避免死锁。scoped_lock 是一个 RAII 风格的锁,当对象离开作用域时,它会自动解锁所有互斥量。不支持手动锁定和解锁,也不支持条件变量。它的主要用途是在需要同时锁定多个互斥量时提供简单且安全的解决方案。
有关于相关例子,可以参考文章[例子]。

  • 9
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
shared_lock<std::shared_mutex>是C++用于管理std::shared_mutex的共享的类。它可以在多个线程同时获得对共享资源的读取访问权。\[1\]在使用shared_lock<std::shared_mutex>时,通常会将其与std::shared_mutex一起使用,以实现对共享资源的并发读取操作。\[1\]例如,在dns_cache类的find_entry函数,使用shared_lock<std::shared_mutex>来获取对entries的共享访问权,以允许多个线程同时读取entries的数据。\[2\]这样可以提高并发性能,因为多个线程可以同时读取共享资源而无需互斥定。 #### 引用[.reference_title] - *1* *3* [C++互斥对象std::mutex与std::shared_mutex;互斥:std::lock_guard、std::unique_lock与std::shared_...](https://blog.csdn.net/qq_33726635/article/details/109693403)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [c++之std::unique_lock, std::lock, std::scoped_lock及std::condition_variable](https://blog.csdn.net/weixin_44537992/article/details/122734223)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值