C++11 同步与互斥

石中火本火 / 2023-08-04 / 原文

C++11 同步与互斥

1. std中的锁

1.1 锁是实现互斥的方法,在std中实现了多种基本锁如下:

  1. std::mutex:最基本的互斥锁,只能在同一线程中进行加锁和解锁操作。

  2. std::recursive_mutex:递归互斥锁,允许同一线程多次加锁,但必须在同一线程中解锁相同次数。

  3. std::timed_mutex:定时互斥锁,允许线程尝试加锁一段时间,如果超时则放弃锁。

  4. std::recursive_timed_mutex:递归定时互斥锁,结合了递归和定时互斥锁的特性。

  5. std::shared_mutex:共享互斥锁,允许多个线程同时读取共享资源,但只允许一个线程写入共享资源。当一个线程持有写入权限时,其他所有线程都无法获取读取或写入权限,直到该线程释放写入权限。注意这是一个读写分离锁,即多个线程可以同时读取共享数据,但只有一个线程可以写入共享数据。当一个线程正在写入共享数据时,其他线程必须等待该线程完成写入操作后才能进行读取或写入操作。

  6. std::shared_timed_mutex:定时共享互斥锁,结合了共享和定时互斥锁的特性。

    这些锁类型都是线程安全的,可以在多线程环境下使用。选择哪种锁类型取决于具体的应用场景和需求。

1.2 锁的封装

为了更方便的管理互斥锁的生命周期,具有RAII特性,即在对象构造时获取资源,在对象析构时释放资源,从而保证资源的正确获取和释放。他利用此原理在析构函数中释放了锁,即可以达到自动释放锁的效果。

  • std::unique_lock<std::mutex> lock(mutex_)

    可以在构造函数中传入一个互斥锁对象,当std::unique_lock对象被销毁时(出了作用域会自动销毁),它会自动释放互斥锁。同时,std::unique_lock还提供了lock()unlock()方法,可以手动控制互斥锁的加锁和解锁。

    /*unique_lock简单实现*/
    template<class Mutex>
    class unique_lock { 
    public:
    	explicit unique_lock(Mutex& m) : mutex_(m) {
        	mutex_.lock();    
        }   
        ~unique_lock() {  
        	mutex_.unlock();    
        } 
    private: 
    	Mutex& mutex_; 
    };
    
  • std::lock_guard<std::mutex> lock(mutex_).当std::lock_guard对象被销毁时,互斥锁会自动解锁。这样可以确保在std::lock_guard对象的生命周期内,互斥锁一直处于锁定状态,从而避免了忘记解锁的问题。

  • std::scoped_lock也是一个模板类,用于管理多个互斥锁。在创建std::scoped_lock对象时,需要传入多个互斥锁对象,该对象会在std::scoped_lock的构造函数中被锁定,当std::scoped_lock对象被销毁时,所有互斥锁会自动解锁。这样可以确保在std::scoped_lock对象的生命周期内,所有互斥锁都处于锁定状态,从而避免了死锁的问题。需要注意的是,std::scoped_lock可以同时锁定多个互斥锁,但是不能锁定同一个互斥锁多次,否则会导致死锁。