2023年11月24日

C++11 新特性:锁


在多线程编程中,锁是用于控制对共享资源的访问的一种机制。C++11 引入了一系列新特性来支持线程同步,提供了更高效、更安全的锁管理。本文将介绍 C++11 中的三种重要锁类:std::mutexstd::lock_guardstd::unique_lock,以及它们的使用方法和特点。

1. std::mutex

std::mutex 是 C++11 引入的标准互斥锁,用于保护共享资源,防止多个线程同时访问引发的数据竞争。std::mutex 提供了基本的锁功能,允许一个线程独占对共享资源的访问权。

示例:

#include <iostream>#include <thread>#include <mutex>std::mutex mtx;void print_message(const std::string& message) { std::lock_guard<std::mutex> lock(mtx); // 加锁 std::cout << message << std::endl;}

在这个示例中,std::mutex 对象 mtx 用于保护 print_message 函数中的 std::cout,确保同一时间只有一个线程可以打印消息。

2. std::lock_guard

std::lock_guard 是一种基于 RAII(资源获取即初始化) 的简单锁管理器。它在构造时自动获取锁,在作用域结束时自动释放锁,确保锁在临界区内被正确持有和释放。

示例:

#include <iostream>#include <thread>#include <mutex>std::mutex mtx;void example() { std::lock_guard<std::mutex> lock(mtx); // 自动加锁 // 临界区操作 std::cout << "In critical section" << std::endl;} // lock 离开作用域时自动解锁

在这个示例中,std::lock_guard 对象 lock 在创建时自动加锁,离开作用域时自动解锁,简化了锁的管理。

3. std::unique_lock

std::unique_lock 是一个更灵活的锁管理器,相比 std::lock_guard,它支持延迟加锁、手动解锁和条件变量等功能,适用于更复杂的锁控制场景。

主要功能:

  • 延迟加锁:可以在创建时不立即加锁,稍后通过 lock() 方法手动加锁。

  • 显式解锁:可以通过 unlock() 方法手动解锁,允许在需要时提前释放锁。

  • 所有权转移:支持通过 std::move 将锁的所有权从一个 std::unique_lock 实例转移到另一个。

示例:

延迟加锁

#include <iostream>#include <thread>#include <mutex>std::mutex mtx;void example() { std::unique_lock<std::mutex> lock(mtx, std::defer_lock); // 延迟加锁 lock.lock(); // 手动加锁 // 临界区操作 std::cout << "In critical section" << std::endl; lock.unlock(); // 手动解锁} // lock 离开作用域时自动解锁

在这个示例中,std::unique_lock 被初始化为延迟加锁,锁在需要时显式加锁和解锁。

锁的所有权转移

#include <iostream>#include <thread>#include <mutex>void func(std::unique_lock<std::mutex> lock) { // lock 持有互斥锁 std::cout << "In critical section in func" << std::endl;} // lock 离开作用域时解锁int main() { std::mutex mtx; std::unique_lock<std::mutex> lock(mtx); // 加锁 func(std::move(lock)); // 将锁的所有权转移到 func // lock 在这里是将亡值,已经不再持有锁}

在这个示例中,std::move(lock) 将锁的所有权传递给 func 函数。func 函数在其作用域内持有锁,main 函数中的 lock 变成将亡值,不能再使用。

总结

C++11 引入的锁机制使多线程编程变得更加简单和安全:

  • std::mutex 提供基本的互斥功能,用于保护共享资源。

  • std::lock_guard 基于 RAII 机制,简化了锁的管理,确保在作用域内持有锁。

  • std::unique_lock 提供了更高的灵活性,支持延迟加锁、手动解锁和锁所有权转移,适用于更复杂的锁控制需求。

了解这些新特性,并根据实际需要选择合适的锁管理方式,将帮助你在多线程编程中更有效地管理共享资源和确保线程安全。