当前位置: 首页 > news >正文

C++ mutex 锁的使用

Mutex介绍

在 C++ 中,std::mutex 是标准库提供的用于线程同步的互斥锁(mutex)用于保护共享资源,防止多个线程同时访问造成数据竞争(data race)。下面将详细介绍 std::mutex 的使用方法,包括基本用法、RAII 风格的锁管理(如 std::lock_guard 和 std::unique_lock)等。

一、基本用法:直接使用 std::mutex

示例代码:

#include <iostream>
#include <thread>
#include <mutex>std::mutex mtx; // 定义一个全局互斥锁
int shared_data = 0; // 共享数据void increment() {for (int i = 0; i < 100000; ++i) {mtx.lock();      // 加锁++shared_data;   // 访问共享数据mtx.unlock();    // 解锁}
}int main() {std::thread t1(increment);std::thread t2(increment);t1.join();t2.join();std::cout << "Final shared_data: " << shared_data << std::endl;return 0;
}

说明:

  • mtx.lock():加锁,如果锁已被其他线程持有,则当前线程会阻塞,直到锁被释放。
  • mtx.unlock():解锁,释放锁,允许其他线程获取该锁。
  • 注意:必须确保每次 lock() 后都有对应的 unlock(),否则可能导致死锁或其他线程永远无法获取锁。

二、使用 RAII 风格的锁管理:std::lock_guard

为了避免忘记解锁导致的潜在问题,推荐使用 RAII(Resource Acquisition Is Initialization)风格的锁管理工具,如 std::lock_guard。它在构造时自动加锁在析构时自动解锁,即使发生异常也能保证锁被释放。

示例代码:

#include <iostream>
#include <thread>
#include <mutex>std::mutex mtx;
int shared_data = 0;void increment() {for (int i = 0; i < 100000; ++i) {std::lock_guard<std::mutex> lock(mtx); // 构造时加锁,析构时解锁++shared_data;}
}int main() {std::thread t1(increment);std::thread t2(increment);t1.join();t2.join();std::cout << "Final shared_data: " << shared_data << std::endl;return 0;
}

优点:

  • 自动管理锁的生命周期​:无需手动调用 lock() 和 unlock(),减少出错的可能。
  • 异常安全​:即使函数中抛出异常,lock_guard 的析构函数也会被调用,确保锁被释放。

三、更灵活的锁管理:std::unique_lock

std::unique_lock 比 std::lock_guard 更加灵活支持延迟加锁、手动解锁、锁的所有权转移等高级功能。但相应地,它的开销也略大于 std::lock_guard

示例代码:

#include <iostream>
#include <thread>
#include <mutex>std::mutex mtx;
int shared_data = 0;void increment() {for (int i = 0; i < 100000; ++i) {std::unique_lock<std::mutex> lock(mtx); // 构造时加锁++shared_data;lock.unlock(); // 手动解锁(可选)// 这里可以进行一些不需要锁保护的操作lock.lock();   // 再次加锁(可选)// 继续访问共享数据}
}int main() {std::thread t1(increment);std::thread t2(increment);t1.join();t2.join();std::cout << "Final shared_data: " << shared_data << std::endl;return 0;
}

说明:

  • std::unique_lock 提供了更多的控制,比如可以在需要时手动加锁和解锁。
  • 适用于需要更细粒度控制锁的场景,但一般情况下 std::lock_guard 已经足够。

四、多个互斥锁的管理:std::lock

当需要同时锁定多个互斥锁时,为了避免死锁,推荐使用 std::lock 函数,它可以一次性锁定多个互斥锁,并且内部实现了死锁避免算法。

示例代码:

#include <iostream>
#include <thread>
#include <mutex>std::mutex mtx1;
std::mutex mtx2;
int data1 = 0;
int data2 = 0;void process_data() {std::lock(mtx1, mtx2); // 同时锁定两个互斥锁,避免死锁// 使用 std::lock_guard 的适配器 std::adopt_lock,表示互斥锁已经被锁定std::lock_guard<std::mutex> lock1(mtx1, std::adopt_lock);std::lock_guard<std::mutex> lock2(mtx2, std::adopt_lock);// 访问共享数据++data1;++data2;
}int main() {std::thread t1(process_data);std::thread t2(process_data);t1.join();t2.join();std::cout << "Final data1: " << data1 << ", data2: " << data2 << std::endl;return 0;
}

说明:

  • std::lock(mtx1, mtx2):同时锁定 mtx1 和 mtx2,内部使用死锁避免算法,确保不会发生死锁。
  • std::adopt_lock:告诉 std::lock_guard 互斥锁已经被锁定,不需要再次加锁,lock_guard 只负责解锁。

五、其他相关工具

1. std::try_lock

std::try_lock 尝试锁定一个或多个互斥锁,如果无法立即锁定,则不会阻塞,而是返回失败状态。适用于需要非阻塞锁的场景。

2. std::recursive_mutex

如果同一个线程需要多次获取同一个互斥锁,可以使用 std::recursive_mutex,它允许同一线程多次加锁而不会导致死锁。但需谨慎使用,避免逻辑错误。

六、总结

在 C++ 中使用 std::mutex 进行线程同步的基本步骤如下:

  1. 定义互斥锁​:通常定义为全局变量或在需要保护的类中作为成员变量。
  2. 加锁和解锁​:
    • 直接使用 mtx.lock() 和 mtx.unlock()(不推荐,容易出错)。
    • 使用 std::lock_guard(推荐,简单安全)。
    • 使用 std::unique_lock(适用于需要更灵活控制的场景)。
  3. 锁定多个互斥锁​:使用 std::lock 避免死锁。
  4. 避免死锁​:确保锁的获取顺序一致,或使用 std::lock 等工具。

通过合理使用 std::mutex 及其相关的 RAII 工具,可以有效地实现线程间的同步,保护共享资源,避免数据竞争和死锁问题。

七.案例

mutex.h
#ifndef _PROJECT_DEMO0525_MUTEX_TEST_H
#define _PROJECT_DEMO0525_MUTEX_TEST_H#include <thread>
#include <mutex>
#include <iostream>using namespace std;extern mutex mtx;  // 互斥量extern int sharedResource;  // 共享资源void testMutex();void getTestResult();#endif // _PROJECT_DEMO0525_MUTEX_TEST_H
mutex.cpp
#include "mutex_test.h"mutex mtx;  // 互斥量int sharedResource = 0;  // 共享资源const int sum = 1000000;void testMutex()
{for (int i = 0; i < sum; i++){mtx.lock(); // 手动锁定互斥量++sharedResource;  // 访问共享资源mtx.unlock();  // 手动解锁互斥量}}void getTestResult()
{cout << "Final value of sharedResource :" << sharedResource << endl;
}
main.cpp
#include "mutex_test.h"void threadFunction()
{testMutex();
}int main()
{const int numThreads = 100;  // 线程数thread threads[numThreads];  // 定义个线程数组for (int i = 0; i < numThreads; i++)
{threads[i] = thread(threadFunction);
}for (int i = 0; i < numThreads; i++)
{if(threads[i].joinable())threads[i].join();}cout << "All threads have finished ... " << endl;getTestResult();}

最后输出结果如下:

All threads have finished ...
Final value of sharedResource :100000000

相关文章:

  • day27/60
  • 在鸿蒙HarmonyOS 5中实现抖音风格的草稿箱功能
  • 新能源知识库(34)什么是单一制和两部制
  • 经典的多位gpio初始化操作
  • JetBrains IntelliJ IDEA插件推荐
  • Spring MVC 核心枢纽:DispatcherServlet 的深度解析与实践价值
  • Zynq multi boot及网口远程更新开发
  • .Net框架,除了EF还有很多很多......
  • 简易版抽奖活动的设计技术方案
  • 数据库管理与高可用-PostgreSQL初体验
  • 安全编程期末复习34(红色重点向下兼容)
  • 8.1.排序的基本概念
  • ArkUI-X平台差异化
  • 函数中的Callable
  • Web安全漏洞详解及解决方案
  • 行业 |5G六年,互联网改变了什么?
  • Vue 2.0 + C# + OnlyOffice 开发
  • GO自带日志库log包解释
  • RAG->大模型搜索search-R1
  • Java中高并发线程池的相关面试题详解
  • 做车身拉花的网站/如何发布自己的广告
  • 个人品牌网站建设/网络推广好做吗?
  • 诸城做网站的公司/热狗seo优化外包
  • 温州做外贸网站/加强服务保障 满足群众急需需求
  • 网站底部版权html代码/新产品的推广销售方法
  • 不锈钢网站哪家最专业/网络推广项目代理