【C++】互斥锁-①

互斥锁

相比std::lock_guard的优势:

功能 std::lock_guard std::unique_lock
自动加锁+自动解锁 支持 支持
手动加锁/解锁 不支持 支持
延迟加锁(defer_lock) 不支持 支持
条件变量wait支持 不支持 支持
可移动(不可复制) 不可移动 可移动,不可复制

 

延迟加锁用法:

① 条件判断后决定是否加锁,例如有些条件根本不需要加锁,就可以延迟决定是否加锁:

void maybe_lock(bool need_lock) {
    std::unique_lock<std::mutex> lock(mtx, std::defer_lock);

    if (need_lock) {
        lock.lock();
        // 访问共享数据
    } else {
        // 不加锁直接返回或做别的事
    }
}
void maybe_lock(bool need_lock) {
    std::unique_lock<std::mutex> lock(mtx, std::defer_lock);

    if (need_lock) {
        lock.lock();
        // 访问共享数据
    } else {
        // 不加锁直接返回或做别的事
    }
}

② 推迟加锁和手动lock()/unlock()配合,可以控制锁的使用范围:

void custom_lock_region() {
    std::unique_lock<std::mutex> lock(mtx, std::defer_lock);

    // ... 不加锁做前置工作

    lock.lock();   // 开始临界区
    // ... 加锁访问共享数据
    lock.unlock(); // 提前解锁

    // 后面工作不需要锁
}

 

直接使用mtx.lock()出现异常风险:

void risky() {
    mtx.lock();
    do_something();  // 这里如果抛异常,mtx 永远不解锁!
    mtx.unlock();
}

用std::unique_lock是异常安全的:

void safe() {
    std::unique_lock<std::mutex> lock(mtx);  // 自动加锁
    do_something();  // 即使这里抛异常,析构时 lock 自动解锁
}

 

示例:线程安全的计数器

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

int counter = 0;
std::mutex counter_mutex;

void increment(int id) {
    for (int i = 0; i < 5; ++i) {
        std::unique_lock<std::mutex> lock(counter_mutex);  // 自动加锁
        ++counter;
        std::cout << "Thread " << id << " incremented counter to " << counter << std::endl;
        // 自动解锁在 lock 离开作用域时(即函数体末尾)
    }
}

int main() {
    std::thread t1(increment, 1);
    std::thread t2(increment, 2);

    t1.join();
    t2.join();

    std::cout << "Final counter value: " << counter << std::endl;
    return 0;
}

1. 例子:

#include <iostream>
#include <vector>
#include <string>
#include <mutex>
#include <thread>
#include <chrono>

std::mutex log_mutex;
std::vector<std::string> log_buffer;

void log_message(int thread_id, int message_id) {
    // 创建 unique_lock,但暂时不加锁
    std::unique_lock<std::mutex> lock(log_mutex, std::defer_lock);

    // 模拟一些不需要锁的工作
    std::this_thread::sleep_for(std::chrono::milliseconds(10));

    // 手动加锁
    lock.lock();
    std::string msg = "Thread " + std::to_string(thread_id) + " - Message " + std::to_string(message_id);
    log_buffer.push_back(msg);

    // 可以手动解锁早于作用域结束
    lock.unlock();

    // 模拟后续不依赖锁的工作
    std::this_thread::sleep_for(std::chrono::milliseconds(5));
}

int main() {
    std::vector<std::thread> threads;

    // 启动多个线程写日志
    for (int i = 0; i < 5; ++i) {
        threads.emplace_back([i]() {
            for (int j = 0; j < 3; ++j) {
                log_message(i, j);
            }
        });
    }

    for (auto& t : threads) {
        t.join();
    }

    // 主线程读取日志(也加锁)
    {
        std::unique_lock<std::mutex> lock(log_mutex);
        std::cout << "Final log:\n";
        for (const auto& msg : log_buffer) {
            std::cout << msg << "\n";
        }
    }

    return 0;
}

 

 

 

 

 

 

 

 

 3. 点击【下一步】按钮:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2021-11-12 22:55  苏格拉底的落泪  阅读(224)  评论(0)    收藏  举报