• C++多线程学习(二):多线程通信和锁


    参考引用

    1. 多线程状态

    1.1 线程状态说明

    • 初始化 (lnit):该线程正在被创建
    • 就绪 (Ready):该线程在就绪列表中,等待 CPU 调度
    • 运行 (Running):该线程正在运行
    • 阻塞 (Blocked):该线程被阻塞挂起,Blocked 状态包括
      • pend (锁、事件、信号量等阻塞)
      • suspend (主动 pend)
      • delay (延时阻塞)
      • pendtime (因为锁、事件、信号量时间等超时)
    • 退出 (Exit):该线程运行结束,等待父线程回收其控制块资源
      • 告诉操作系统把该线程相关资源释放,不包含堆中的资源释放

    在这里插入图片描述

    1.2 竞争状态和临界区

    • 竞争状态 (Race Condition)
      • 多线程同时读写共享数据
    • 临界区 (Critical Section)
      • 读写共享数据的代码片段(lock 和 unlock 之间的代码)

    避免竞争状态策略,对临界区进行保护,同时只能有一个线程进入临界区

    2. 互斥体和锁

    2.1 互斥锁

    • thread_mutex.cpp
      #include 
      #include 
      #include 
      #include 
      
      using namespace std;
      
      static mutex mux;
      
      void TestThread() {
          for (;;)
          {	
              // 获取锁资源,如果没有则阻塞等待(一次只能有一个线程拿到锁)
              // 拿锁的原则:尽晚申请、尽早释放
              //mux.lock();           // 拿锁方式一 
              if (!mux.try_lock()) {  // 拿锁方式二:可以看到多个进程在竞争拿锁的情况
                  cout << "." << flush;
                  this_thread::sleep_for(100ms);
          
                  continue;
              }
          
              // 业务代码
              cout << "=========" << endl;
              cout << "Test 001" << endl;
              cout << "Test 002" << endl;
              cout << "Test 003" << endl;
              cout << "=========" << endl;
      
              mux.unlock();  // 如果忘记释放锁,则会导致死锁,所有线程都在等待
              this_thread::sleep_for(1000ms);
          }
      }
      
      int main(int argc, char* argv[]) {
          // 同时创建 10 个线程
          for (int i = 0; i < 10; i++) {
              thread th(TestThread);
              th.detach();
          }
          
          getchar();
          
          return 0;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45

    2.2 线程抢占不到资源

    • thread_mutex2.cpp
      #include 
      #include 
      #include 
      #include 
      
      using namespace std;
      
      static mutex mux;
      
      void ThreadMainMux(int i) {
          for (;;)
          {	
              mux.lock();
              cout << i << "[in]" << endl;
              this_thread::sleep_for(1000ms);
              mux.unlock();
              // 防止 unlock() 还未释放完全就进入下一个 lock(),导致其他线程拿不到锁
              this_thread::sleep_for(1ms);
          }
      }
      
      int main(int argc, char* argv[]) {
          // 同时创建 3 个线程
          for (int i = 0; i < 3; i++) {
              thread th(ThreadMainMux, i + 1);
              th.detach();
          }
      
          getchar();
      
          return 0;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32

    2.3 超时锁 timed_mutex 应用

    • 可以记录锁的获取情况,多次超时,可以记录日志,获取错误情况,避免长时间死锁
    • timed_mutex.cpp
      #include 
      #include 
      #include 
      #include 
      
      using namespace std;
      
      timed_mutex tmux;  // 支持超时的互斥锁
      
      void ThreadMainTime(int i) {
          for (;;)
          {	
              if (!tmux.try_lock_for(chrono::milliseconds(500))) {
                  cout << i << "[try_lock_for timeout]" << endl;
                  continue;
              }
              cout << i << "[in]" << endl;
              this_thread::sleep_for(2000ms);
              tmux.unlock();
              // 防止 unlock() 还未释放完全就进入下一个 lock(),导致其他线程拿不到锁
              this_thread::sleep_for(1ms);
          }
      }
      
      int main(int argc, char* argv[]) {
          getchar();
      
          // 同时创建 3 个线程
          for (int i = 0; i < 3; i++) {
              thread th(ThreadMainTime, i + 1);
              th.detach();
          }
      
          getchar();
      
          return 0;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37

    2.4 递归锁 recursive_mutex(可重入)

    • 同一个线程中的同一把锁可以锁多次,避免一些不必要的死锁
    • 组合业务:用到同一个锁
    • recursive_mutex.cpp
      #include 
      #include 
      #include 
      #include 
      
      using namespace std;
      
      recursive_mutex rmux;  // 支持可重入的互斥锁
      
      void Task1() {
          rmux.lock();
          cout << "task1 [in]" << endl;
          rmux.unlock();
      }
      
      void Task2() {
          rmux.lock();
          cout << "task2 [in]" << endl;
          rmux.unlock();
      }
      
      void ThreadMainRec(int i) {
          for (;;)
          {
              // 加锁几次对应的也要解锁几次
              rmux.lock();
              Task1();
              cout << i << "[in]" << endl;
              this_thread::sleep_for(2000ms);
              Task2();
              rmux.unlock();
              this_thread::sleep_for(1ms);
          }
      }
      
      int main(int argc, char* argv[]) {
          // 同时创建 3 个线程
          for (int i = 0; i < 3; i++) {
              thread th(ThreadMainRec, i + 1);
              th.detach();
          }
          
          getchar();
          
          return 0;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46

    2.5 共享锁 shared_mutex(解决读写问题)

    • c++14 共享超时互斥锁 shared_timed_mutex
    • 如果只有写时需要互斥,读取时不需要,用普通的锁的话如何做
    • 按照如下代码,读取只能有一个线程进入,在很多业务场景中,没有充分利用 CPU 资源

    在这里插入图片描述

    • shared_mutex.cpp
      #include 
      #include 
      #include 
      #include 
      #include 
      
      using namespace std;
      
      shared_timed_mutex stmux;  // 支持可重入的共享锁 C++14
      
      // 读取线程
      void ThreadRead(int i) {
          for (;;)
          {
              stmux.lock_shared();
              cout << i << " Read" << endl;
              this_thread::sleep_for(500ms);
              stmux.unlock_shared();
      
              this_thread::sleep_for(1ms);
          }
      }
      
      // 写入线程
      void ThreadWrite(int i) {
          for (;;)
          {
              stmux.lock_shared();  // 只要没有锁定互斥锁,共享锁都是立即返回
              // 读取数据
              stmux.unlock_shared();
      
              // 互斥锁 写入(同时只能一个线程写入),共享锁和互斥锁都不能进入
              stmux.lock();  
              cout << i << " Write" << endl;
              this_thread::sleep_for(300ms);
              stmux.unlock();
      
              this_thread::sleep_for(1ms);
          }
      }
      
      int main(int argc, char* argv[]) {
          for (int i = 0; i < 3; i++) {
              thread th(ThreadWrite, i + 1);
              th.detach();
          }
      
          for (int i = 0; i < 3; i++) {
              thread th(ThreadRead, i + 1);
              th.detach();
          }
      
          getchar();
      
          return 0;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
  • 相关阅读:
    4年用户数破亿,孙哥带领波场再创新高
    js判断浏览器的类型及指定浏览器类型
    (Research)肝肿瘤免疫微环境亚型和中性粒细胞异质性
    DRF 用户认证
    iText7高级教程之构建基础块——5.使用AbstractElement对象(part 2)
    【无标题】ssm
    Go 语言基础
    太牛了,用Python实现服务部署自动化
    Leetcode 26-30题
    axios的封装
  • 原文地址:https://blog.csdn.net/qq_42994487/article/details/134563769