如何使用lock_guard

本文探讨了C++11中的lock_guard类在多线程环境中的使用,特别是其在异常处理中的行为。通过一个示例展示了如何正确地使用lock_guard来避免并发访问时的数据竞争问题。

lock_gurad 是c++11 提供的一个新类,是一个区域锁, 只在作用域内起作用, 出了作用域就自动解锁。

看起来应该是比较简单的,但是异常配合起来使用就需要多加注意。

下面的代码是cplusplus.com 提供的sample。

这个sample的本意是启动10个线程,每个线程对1个数字判断奇偶, 

如果是偶数就打印 “x is even”(x 是要判断的数字),如果是奇数就打印[exception caught]。

但由于sample 中,  std::lock_guard<std::mutex> lck (mtx) 是在try 的作用域中打印的,

一旦print_even 抛出异常,由catch 捕获到,luck就将解锁。 

有时候,不同线程的 std::cout << "[exception caught]\n"; 和 std::cout << x << " is even\n";

会同时运行, 这样导致 打印出现混乱。

例如:

[6e xicse petvieonn

 caught]

[exception caught]

2 is even

8 is even

4 is even

[exception caught]

[exception caught]

[exception caught]

10 is even

将 std::lock_guard<std::mutex> lck (mtx);

 从try 中移到 try 范围外就没问题了。




在函数 print_thread_id 中在try 中调用 

实际coding , 应该需要判断一下 异常处理和try 中的代码是否会有冲突,

如果没有则sample code  是没问题的, 如果有 还要另想其他方法。


// lock_guard example

#include <iostream>       // std::cout

#include <thread>         // std::thread

#include <mutex>          // std::mutex, std::lock_guard

#include <stdexcept>      // std::logic_error


std::mutex mtx;


void print_even (int x) {

  if (x%2==0) std::cout << x << " is even\n";

  else throw (std::logic_error("not even"));

}


void print_thread_id (int id) {

  std::lock_guard<std::mutex> lck (mtx);

  try {

    // using a local lock_guard to lock mtx guarantees unlocking on destruction / exception:

    //std::lock_guard<std::mutex> lck (mtx);

    print_even(id);

  }

  catch (std::logic_error&) {

    std::cout << "[exception caught]\n";

  }

}


int main ()

{

  std::thread threads[10];

  // spawn 10 threads:

  for (int i=0; i<10; ++i)

    threads[i] = std::thread(print_thread_id,i+1);


  for (auto& th : threads) th.join();


  return 0;

}

~                                                                                                                                                                                                         

~                

<think>嗯,用户想了解如何在C++中使用lock_guard。我之前已经回答过类似的问题,现在需要结合用户提供的引用内容来组织一个详细的回答。首先,我需要确认用户的需求,他们可能对lock_guard的基本用法、RAII机制、如何避免死锁以及与其他锁管理类的区别感兴趣。根据引用中的内容,lock_guard是C++11提供的RAII类模板,用于自动管理互斥量的锁定和解锁。用户提供的代码示例展示了lock_guard在不同情况下的使用,比如在函数内部的作用域,以及如何处理已经手动加锁的情况。此外,还有自定义的my_lock_guard实现,说明其原理是构造时加锁,析构时解锁。 需要注意的是,用户可能混淆了Autolocklock_guard,但根据引用内容,Autolock是某些框架的自定义实现,而lock_guard是标准库的一部分。因此,在回答时应强调标准用法,并指出可能的框架特定实现。接下来,我需要组织回答的结构。首先介绍基本用法,然后说明adopt_lock的用法,接着可能对比与unique_lock的区别,最后提到注意事项和常见问题。同时,要确保引用正确的示例代码,并在适当的位置添加引用标记,如[^1]、[^2]等。 用户可能对如何正确使用lock_guard存在疑问,比如是否需要在多个作用域中使用,或者如何处理已经加锁的互斥量。引用中的例子显示可以在一个函数中使用多个lock_guard作用域,这需要明确说明。此外,引用[3]提到使用std::adopt_lock来接管已锁定的互斥量,避免重复加锁,这点需要详细解释。还需要注意用户可能存在的误区,比如lock_guard的生命周期管理,以及不能拷贝等限制。引用[4]提到lock_guard不管理互斥量的生命周期,必须确保互斥量在lock_guard析构时仍然有效,这点需要强调。 最后,生成相关问题时要基于引用内容,比如unique_locklock_guard的区别、如何避免死锁、自定义RAII锁类等。这些问题可以帮助用户进一步学习相关主题。</think>### C++ `lock_guard` 使用方法详解 #### 1. 基本用法(自动加锁/解锁) `std::lock_guard` 是 C++11 提供的 RAII(资源获取即初始化)锁管理工具,**自动管理互斥量的生命周期**。其核心特性是: - **构造时自动加锁**:在创建 `lock_guard` 对象时立即锁定关联的互斥量 - **析构时自动解锁**:当对象离开作用域时自动释放锁,避免忘记解锁导致的死锁[^1] ```cpp #include <mutex> std::mutex mtx; // 全局互斥量 void thread_safe_function() { std::lock_guard<std::mutex> guard(mtx); // 构造时自动加锁 // 临界区代码(共享资源操作) // ... } // 作用域结束时自动解锁[^1] ``` #### 2. 接管已加锁的互斥量(`adopt_lock` 模式) 当互斥量**已手动加锁**时,需使用 `std::adopt_lock` 参数标记,避免重复加锁: ```cpp void manual_lock_control() { mtx.lock(); // 手动加锁 { std::lock_guard<std::mutex> guard(mtx, std::adopt_lock); // 临界区代码... } // 自动解锁[^3] } ``` #### 3. 多作用域嵌套使用 `lock_guard` 的作用域控制支持**细粒度锁管理**,可在同一函数内多次创建: ```cpp void complex_operation() { { // 第一个临界区 std::lock_guard<std::mutex> lock1(mtx); // 操作1... } { // 第二个临界区 std::lock_guard<std::mutex> lock2(mtx); // 操作2... }[^1] } ``` #### 4. 实现原理与自定义实现 通过分析 `lock_guard` 的简化实现(参考引用[2]),可理解其核心机制: ```cpp template<typename T> class my_lock_guard { public: explicit my_lock_guard(T& mutex) : mutex_(mutex) { mutex_.lock(); // 构造时加锁 } ~my_lock_guard() { mutex_.unlock(); // 析构时解锁 } private: T& mutex_; // 引用传递互斥量 // 禁止拷贝和赋值 my_lock_guard(const my_lock_guard&) = delete; my_lock_guard& operator=(const my_lock_guard&) = delete; }; ``` #### 5. 注意事项 - **生命周期管理**:必须确保互斥量在 `lock_guard` 析构时仍然有效[^4] - **不可拷贝**:`lock_guard` 对象不可拷贝,但可通过作用域控制实现移动语义 - **异常安全**:即使临界区代码抛出异常,仍保证解锁操作[^1] - **递归互斥量**:需使用 `std::recursive_mutex` 配合 `lock_guard` 实现递归锁 #### 6. 典型应用场景 ```cpp // 多线程计数器 std::mutex counter_mutex; int global_counter = 0; void increment_counter() { std::lock_guard<std::mutex> lock(counter_mutex); global_counter++; std::cout << "Counter: " << global_counter << std::endl; }[^5] // 文件写入保护 void safe_file_write(const std::string& data) { static std::mutex file_mutex; std::lock_guard<std::mutex> lock(file_mutex); std::ofstream file("data.log", std::ios::app); file << data << std::endl; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值