• C++ 互斥


    mutex

    mutex 类是能用于保护共享数据免受从多个线程同时访问的同步原语。
    mutex 提供排他性非递归所有权语义:

    • 调用方线程从它成功调用 lock 或 try_lock 开始,到它调用 unlock 为止占有 mutex 。
    • 线程占有 mutex 时,所有其他线程若试图要求 mutex 的所有权,则将阻塞(对于 lock 的调用)或收到 false 返回值(对于 try_lock )。
    • 调用方线程在调用 lock 或 try_lock 前必须不占有 mutex 。
    • 若 mutex 在仍为任何线程所占有时即被销毁,或在占有 mutex 时线程终止,则行为未定义。
    • mutex 类满足互斥体 (Mutex) 和标准布局类型 (StandardLayoutType) 的全部要求。

    std::mutex 既不可复制亦不可移动。

    成员类型
    	native_handle_type(可选)	实现定义
    成员函数
    	(构造函数)	构造互斥(公开成员函数)
    	(析构函数) 	销毁互斥(公开成员函数)
    	operator=[被删除]	不可复制赋值(公开成员函数)
    锁定
    	lock	锁定互斥,若互斥不可用则阻塞(公开成员函数)
    	try_lock	尝试锁定互斥,若互斥不可用则返回(公开成员函数)
    	unlock	解锁互斥(公开成员函数)
    原生句柄
    	native_handle	返回底层实现定义的原生句柄(公开成员函数)
    

    注意:通常不直接使用 std::mutex :std::unique_lock 、 std::lock_guard 或 std::scoped_lock (C++17 起)以更加异常安全的方式管理锁定。

    recursive_mutex

    recursive_mutex 类是同步原语,能用于保护共享数据免受从个多线程同时访问。
    recursive_mutex 提供排他性递归所有权语义:

    • 调用方线程在从它成功调用 lock 或 try_lock 开始的时期里占有 recursive_mutex 。此时期间,线程可以进行对 lock 或 try_lock 的附加调用。所有权的时期在线程调用 unlock 匹配次数时结束。
    • 线程占有 recursive_mutex 时,若其他所有线程试图要求 recursive_mutex 的所有权,则它们将阻塞(对于调用 lock )或收到 false 返回值(对于调用 try_lock )。
    • 可锁定 recursive_mutex 次数的最大值是未指定的,但抵达该数后,对 lock 的调用将抛出std::system_error 而对 try_lock 的调用将返回 false 。

    若 recursive_mutex 在仍为某线程占有时被销毁,则程序行为未定义。 recursive_mutex 类满足互
    斥体 (Mutex) 和标准布局类型 (StandardLayoutType) 的所有要求。

    成员类型
    	native_handle_type(可选)	实现定义
    成员函数
    	(构造函数)	构造互斥(公开成员函数)
    	(析构函数)	销毁互斥(公开成员函数)
    	operator=[被删除] 	不可复制赋值(公开成员函数)
    锁定
    	lock	锁定互斥,若互斥不可用则阻塞(公开成员函数)
    	try_lock	尝试锁定互斥,若互斥不可用则返回(公开成员函数)
    	unlock	解锁互斥(公开成员函数)
    原生句柄
    	native_handle	返回底层实现定义的原生句柄(公开成员函数)
    
    //recursive_mutex 的使用场景之一是保护类中的共享状态,而类的成员函数可能相互调用
    
    运行此代码
    #include <iostream>
    #include <thread>
    #include <mutex>
     
    class X {
        std::recursive_mutex m;
        std::string shared;
      public:
        void fun1() {
          std::lock_guard<std::recursive_mutex> lk(m);
          shared = "fun1";
          std::cout << "in fun1, shared variable is now " << shared << '\n';
        }
        void fun2() {
          std::lock_guard<std::recursive_mutex> lk(m);
          shared = "fun2";
          std::cout << "in fun2, shared variable is now " << shared << '\n';
          fun1(); // 递归锁在此处变得有用
          std::cout << "back in fun2, shared variable is " << shared << '\n';
        };
    };
     
    int main() 
    {
        X x;
        std::thread t1(&X::fun1, &x);
        std::thread t2(&X::fun2, &x);
        t1.join();
        t2.join();
    }
    

    可能的输出:

    in fun1, shared variable is now fun1
    in fun2, shared variable is now fun2
    in fun1, shared variable is now fun1
    back in fun2, shared variable is fun1
    

    shared_mutex

    shared_mutex 类是一个同步原语,可用于保护共享数据不被多个线程同时访问。与便于独占访问的其他互斥类型不同,shared_mutex 拥有二个访问级别:

    • 共享 - 多个线程能共享同一互斥的所有权。
    • 独占性 - 仅一个线程能占有互斥。
    • 若一个线程已获取独占性锁(通过 lock 、 try_lock ),则无其他线程能获取该锁(包括共享的)。

    仅当任何线程均未获取独占性锁时,共享锁能被多个线程获取(通过 lock_shared 、 try_lock_shared )。
    在一个线程内,同一时刻只能获取一个锁(共享或独占性)。
    共享互斥体在能由任何数量的线程同时读共享数据,但一个线程只能在无其他线程同时读写时写同一数据时特别有用。
    shared_mutex 类满足共享互斥体 (SharedMutex) 和标准布局类型 (StandardLayoutType) 的所有要求。

    成员类型
    	native_handle_type(可选)	实现定义
    成员函数
    	(构造函数)	构造互斥(公开成员函数)
    	(析构函数)	销毁互斥(公开成员函数)
    	operator=[被删除]	不可复制赋值(公开成员函数)
    排他性锁定
    	lock	锁定互斥,若互斥不可用则阻塞(公开成员函数)
    	try_lock	尝试锁定互斥,若互斥不可用则返回(公开成员函数)
    	unlock	解锁互斥(公开成员函数)
    共享锁定
    	lock_shared	为共享所有权锁定互斥,若互斥不可用则阻塞(公开成员函数)
    	try_lock_shared	尝试为共享所有权锁定互斥,若互斥不可用则返回(公开成员函数)
    	unlock_shared	解锁互斥(共享所有权)(公开成员函数)
    原生句柄
    	native_handle	返回底层实现定义的原生句柄(公开成员函数)
    
    #include <iostream>
    #include <mutex>  // 对于 std::unique_lock
    #include <shared_mutex>
    #include <thread>
     
    class ThreadSafeCounter {
     public:
      ThreadSafeCounter() = default;
     
      // 多个线程/读者能同时读计数器的值。
      unsigned int get() const {
        std::shared_lock<std::shared_mutex> lock(mutex_);
        return value_;
      }
     
      // 只有一个线程/写者能增加/写线程的值。
      void increment() {
        std::unique_lock<std::shared_mutex> lock(mutex_);
        value_++;
      }
     
      // 只有一个线程/写者能重置/写线程的值。
      void reset() {
        std::unique_lock<std::shared_mutex> lock(mutex_);
        value_ = 0;
      }
     
     private:
      mutable std::shared_mutex mutex_;
      unsigned int value_ = 0;
    };
     
    int main() {
      ThreadSafeCounter counter;
     
      auto increment_and_print = [&counter]() {
        for (int i = 0; i < 3; i++) {
          counter.increment();
          std::cout << std::this_thread::get_id() << ' ' << counter.get() << '\n';
     
          // 注意:写入 std::cout 实际上也要由另一互斥同步。省略它以保持示例简洁。
        }
      };
     
      std::thread thread1(increment_and_print);
      std::thread thread2(increment_and_print);
     
      thread1.join();
      thread2.join();
    }
     
    // 解释:下列输出在单核机器上生成。 thread1 开始时,它首次进入循环并调用 increment() ,
    // 随后调用 get() 。然而,在它能打印返回值到 std::cout 前,调度器将 thread1 置于休眠
    // 并唤醒 thread2 ,它显然有足够时间一次运行全部三个循环迭代。再回到 thread1 ,它仍在首个
    // 循环迭代中,它最终打印其局部的计数器副本的值,即 1 到 std::cout ,再运行剩下二个循环。
    // 多核机器上,没有线程被置于休眠,且输出更可能为递增顺序。
    

    可能的输出:

    单核:
    123084176803584 2
    123084176803584 3
    123084176803584 4
    123084185655040 1
    123084185655040 5
    123084185655040 6
    多核:
    140623314495232 2
    140623314495232 3
    140623314495232 4
    140623306102528 4
    140623306102528 5
    140623306102528 6
    
  • 相关阅读:
    Spring ApplicationContext 是如何被注入的
    Spring @Controller和@RestController的区别?
    怎样查看 spring IoC 容器有哪些 bean
    YYKit持续补丁 brave
    iOS如何实现多代理模式OC brave
    iOS 之获取IP地址 以及 判断个人热点是否开启 brave
    CGContext 和 CIContext brave
    android 调用微信QQ的其他应用打开列表中添加自己的应用,并且获取uri brave
    CGContext图形上下文详解 brave
    iOS scrollView如何居中缩放 brave
  • 原文地址:https://www.cnblogs.com/chengmf/p/15957646.html
Copyright © 2020-2023  润新知