C++并发编程实战:深入理解互斥量(Mutex)的使用

C++并发编程实战:深入理解互斥量(Mutex)的使用

Cplusplus-Concurrency-In-Practice A Detailed Cplusplus Concurrency Tutorial 《C++ 并发编程指南》 Cplusplus-Concurrency-In-Practice 项目地址: https://gitcode.com/gh_mirrors/cp/Cplusplus-Concurrency-In-Practice

互斥量基础概念

在多线程编程中,互斥量(Mutex)是最基本的同步机制之一,用于保护共享数据免受并发访问的破坏。C++11标准库提供了多种互斥量类型,满足不同场景下的线程同步需求。

std::mutex详解

std::mutex是C++中最基本的互斥量类型,它提供了独占所有权的特性。理解std::mutex的核心要点:

  1. 基本特性

    • 不支持递归上锁(同一线程多次锁定会导致死锁)
    • 不可拷贝或移动
    • 初始状态为未锁定(unlocked)
  2. 关键成员函数

    • lock():阻塞式获取锁
    • unlock():释放锁
    • try_lock():非阻塞式尝试获取锁

使用示例

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

std::mutex mtx;
int shared_data = 0;

void increment() {
    for (int i = 0; i < 10000; ++i) {
        mtx.lock();
        ++shared_data;
        mtx.unlock();
    }
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);
    
    t1.join();
    t2.join();
    
    std::cout << "Final value: " << shared_data << std::endl;
    return 0;
}

注意事项

  • 必须确保每次lock()都有对应的unlock()
  • 忘记解锁会导致死锁
  • 异常安全:如果lock()unlock()之间抛出异常,可能导致锁无法释放

递归互斥量 std::recursive_mutex

std::recursive_mutex解决了同一线程需要多次获取同一锁的问题:

  1. 特点

    • 允许同一线程多次锁定
    • 需要相同次数的解锁操作
    • 性能略低于普通互斥量
  2. 适用场景

    • 递归函数调用
    • 需要调用多个可能获取同一锁的函数
#include <iostream>
#include <thread>
#include <mutex>

std::recursive_mutex rmtx;

void recursive_function(int level) {
    rmtx.lock();
    std::cout << "Level " << level << " locked" << std::endl;
    
    if (level > 0) {
        recursive_function(level - 1);
    }
    
    std::cout << "Level " << level << " unlocked" << std::endl;
    rmtx.unlock();
}

int main() {
    std::thread t(recursive_function, 3);
    t.join();
    return 0;
}

定时互斥量 std::timed_mutex

std::timed_mutex在普通互斥量基础上增加了超时功能:

  1. 新增功能

    • try_lock_for():尝试在指定时间段内获取锁
    • try_lock_until():尝试在指定时间点前获取锁
  2. 使用场景

    • 需要避免长时间阻塞的场合
    • 实现带超时的资源访问
#include <iostream>
#include <chrono>
#include <thread>
#include <mutex>

std::timed_mutex tmtx;

void timed_task(int id) {
    using namespace std::chrono;
    
    if (tmtx.try_lock_for(seconds(1))) {
        std::cout << "Thread " << id << " got the lock" << std::endl;
        std::this_thread::sleep_for(milliseconds(500));
        tmtx.unlock();
    } else {
        std::cout << "Thread " << id << " couldn't get the lock" << std::endl;
    }
}

int main() {
    std::thread t1(timed_task, 1);
    std::thread t2(timed_task, 2);
    
    t1.join();
    t2.join();
    return 0;
}

锁保护类:std::lock_guard和std::unique_lock

为了避免手动管理锁的生命周期,C++提供了RAII风格的锁保护类。

std::lock_guard

最简单的锁保护机制,构造时加锁,析构时解锁。

特点

  • 不支持手动解锁
  • 不支持锁的所有权转移
  • 轻量级,性能好
std::mutex mtx;

void safe_increment() {
    std::lock_guard<std::mutex> lock(mtx);
    // 临界区代码
    // 离开作用域自动解锁
}

std::unique_lock

更灵活的锁保护机制,提供更多控制选项。

特点

  • 支持延迟锁定
  • 支持手动解锁
  • 支持锁的所有权转移
  • 支持条件变量
  • 比lock_guard稍重
std::mutex mtx;

void flexible_task() {
    std::unique_lock<std::mutex> lock(mtx, std::defer_lock);
    
    // 做一些不需要锁的工作...
    
    lock.lock(); // 需要时手动锁定
    // 临界区代码
    lock.unlock(); // 可以手动解锁
    
    // 做一些不需要锁的工作...
    
    if (needs_lock_again) {
        lock.lock();
        // 更多临界区代码
    }
    // 离开作用域自动处理
}

最佳实践建议

  1. 优先使用RAII锁:尽可能使用lock_guardunique_lock而非直接操作mutex
  2. 锁的粒度:保持锁的粒度尽可能小,减少持有锁的时间
  3. 避免死锁
    • 按固定顺序获取多个锁
    • 使用std::lock()函数同时锁定多个互斥量
  4. 性能考虑
    • 只在必要时使用递归锁
    • 评估锁竞争情况,考虑更高级的同步机制

通过合理使用这些互斥量类型和锁保护机制,可以构建出既安全又高效的并发程序。理解每种工具的特性和适用场景,是成为优秀C++并发程序员的关键一步。

Cplusplus-Concurrency-In-Practice A Detailed Cplusplus Concurrency Tutorial 《C++ 并发编程指南》 Cplusplus-Concurrency-In-Practice 项目地址: https://gitcode.com/gh_mirrors/cp/Cplusplus-Concurrency-In-Practice

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

贺俭艾Kenyon

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值