• C++多线程学习(一):C++11 多线程快速入门


    参考引用

    1. 为什么要用多线程

    • 任务分解
      • 耗时的操作,任务分解,实时响应
    • 数据分解
      • 充分利用多核CPU处理数据
    • 数据流分解
      • 读写分离,解耦合设计

    2. 第一个子线程代码示例

    • first_thread.cpp
      #include 
      #include 
      
      using namespace std;
      
      // 创建的子线程的入口函数
      void ThreadMain() {
          // 获取子线程开始时刻的 ID 号
          cout << "begin sub thread ID " << this_thread::get_id() << endl;
          for (int i = 0; i < 5; i++) {
              cout << "in thread " << i << endl;
              // 子线程睡眠(释放)CPU 资源 1000ms
              this_thread::sleep_for(chrono::seconds(1));  
          }
          cout << "end sub thread ID " << this_thread::get_id() << endl;
      }
      
      // 主线程的入口函数 main()
      int main(int argc, char* argv[]) {
          cout << "main thread ID " << this_thread::get_id() << endl;
      
          // 线程创建启动
          thread th(ThreadMain);
          cout << "begin wait sub thread" << endl;
      
          // 阻塞等待子线程退出
          th.join();
          cout << "end wait sub thread" << endl;
      
          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
    • 控制台输出
      main thread ID 21580
      begin wait sub thread
      begin sub thread ID 22924
      in thread 0
      in thread 1
      in thread 2
      in thread 3
      in thread 4
      end sub thread ID 22924
      end wait sub thread
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10

    3. std::thread 对象生命周期、线程等待和分离

    • thread_detach.cpp

      #include 
      #include 
      
      using namespace std;
      
      bool is_exit = false;
      
      void ThreadMain() {
          cout << "begin sub thread ID " << this_thread::get_id() << endl;
          for (int i = 0; i < 5; i++) {
              if (!is_exit)
                  break;
              cout << "in thread " << i << endl;
              this_thread::sleep_for(chrono::seconds(1));  // 子线程睡眠(释放)CPU 资源 1000ms
          }
          cout << "end sub thread ID " << this_thread::get_id() << endl;
      }
      
      int main(int argc, char* argv[]) {
          {
              //thread th(ThreadMain); // 出错,thread 对象被销毁 子线程还在运行
          }
      
          {
              thread th(ThreadMain);
              th.detach();  // 子线程与主线程分离 守护线程(在后台运行)
              // 但存在一个问题:主线程退出后 子线程不一定退出
          }
      
          {
              thread th(ThreadMain);
              this_thread::sleep_for(chrono::seconds(1));//1000ms
              is_exit = true;  // 通知子线程退出
              cout << "主线程阻塞,等待子线程退出" << endl;
              th.join();       // 主线程阻塞,等待子线程退出
              cout << "子线程已经退出!" << endl;
          }
      
          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
    • 控制台输出

      begin sub thread ID 25520
      end sub thread ID 25520
      begin sub thread ID 23324
      end sub thread ID 23324
      主线程阻塞,等待子线程退出
      子线程已经退出!
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6

    4. 全局函数作为线程入口

    • thread_para.cpp

      #include 
      #include 
      #include 
      
      using namespace std;
      
      class Para {
      public:
          Para() {
              cout << "Create Para" << endl;
          }
          Para(const Para& p) {  // 拷贝构造函数
              cout << "Copy Para" << endl; 
          }
          ~Para() {
              cout << "Drop Para" << endl;
          }
      
          string name;
      };
      
      void ThreadMain(int p1, float p2, string str, Para p4) {
          this_thread::sleep_for(100ms);
          cout << "ThreadMain " << p1 << " " << p2 << " " << str << " " << p4.name << endl;
      }
      
      int main(int argc, char* argv[]) {
          thread th;
          {
              float f1 = 12.1f;
              Para p;
              p.name = "test Para class";
              // 所有的参数做复制
              th = thread(ThreadMain, 101, f1, "test string para", p);
          }
          th.join();
      
          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
    • 控制台输出

      Create Para
      Copy Para
      Drop Para
      Copy Para
      ThreadMain 101 12.1 test string para
      Drop Para
      Drop Para
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7

    5. 线程函数传递指针和引用

    • 参数传递存在的问题

      • 传递空间已经销毁
      • 多线程共享访问一块空间
      • 传递的指针变量的生命周期小于线程
    • thread_para.cpp

    #include 
    #include 
    #include 
    
    using namespace std;
    
    class Para {
    public:
        Para() { cout << "Create Para" << endl; }
        Para(const Para& p) { cout << "Copy Para" << endl; }
        ~Para() { cout << "Drop Para" << endl; }
        string name;
    };
    
    void ThreadMain(int p1, float p2, string str, Para p4) {
        this_thread::sleep_for(100ms);
        cout << "ThreadMain " << p1 << " " << p2 << " " << str <<" "<<p4.name<< endl;
    }
    
    void ThreadMainPtr(Para* p) {
        this_thread::sleep_for(100ms);
        cout << "ThreadMainPtr name = " << p->name << endl;
    }
    
    void ThreadMainRef(Para& p) {
        this_thread::sleep_for(100ms);
        cout << "ThreadMainPtr name = " << p.name << endl;
    }
    int main(int argc, char* argv[]) {
        {
            // 传递引用
            Para p;
            p.name = "test ref";
            thread th(ThreadMainRef, ref(p));
            th.join();
        }
        getchar();
    
        {
            // 传递线程指针
            Para p;
            p.name = "test ThreadMainPtr name";
            thread th(ThreadMainPtr, &p);  
            th.detach();  // 错误,线程访问的 p 空间会提前释放
        }
        getchar();  // Para 释放
    
        {  
            // 传递线程指针
            Para p;
            p.name = "test ThreadMainPtr name";
            thread th(ThreadMainPtr, &p);
            th.join();
            getchar();
        }
    
        thread th;
        {
            float f1 = 12.1f;
            Para p;
            p.name = "test Para class";
            // 所有的参数做复制
            th =  thread(ThreadMain, 101, f1, "test string para", p);
        }
        th.join();
    
        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
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 控制台输出
      Create Para
      ThreadMainPtr name = test ref
      Drop Para
      
      • 1
      • 2
      • 3

    6. 成员函数作为线程入口–封装线程基类接口

    • XThread.h

      • 封装线程基类接口
      #pragma once
      
      #include 
      #include 
      
      class XThread {
      public:
          virtual void Start() {
              is_exit_ = false;
              th_ = std::thread(&XThread::Main, this);
          }
          virtual void Stop() {
              is_exit_ = true;
              Wait();
          }
          virtual void Wait() {
              if (th_.joinable())
                  th_.join();
          }
          bool is_exit() {
              return is_exit_;
          }
      
      private:
          virtual void Main() = 0;  // 纯虚函数必须要在基类中实现
          std::thread th_;
          bool is_exit_ = false;    // 符合谷歌代码风格,私有成员变量后缀加 _
      };
      
      • 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
    • thread_class.cpp

      #include 
      #include 
      #include 
      #include "XThread.h"
      
      using namespace std;
      
      /*
      class MyThread {
      public:
          // 入口线程函数
          void Main() {
              cout << "MyThread Main" << name << ": " << age;
          }
      
          string name;
          int age = 0;
      };
      */
      
      class TestXThread : public XThread {
      public:
          void Main() override {
              cout << "TestXThread Main begin" << endl;
              while (!is_exit()) {
                  this_thread::sleep_for(100ms);
                  cout << "." << flush;  // 添加 flush 是为了确保 . 正常输出
              }
              cout << "\nTestXThread Main end" << endl;
          }
          string name;
      };
      
      int main(int argc, char* argv[]) {
          TestXThread testth;
          testth.name = "TestXThread name ";
          testth.Start();
          this_thread::sleep_for(3s);
          testth.Stop();
      
          testth.Wait();
          getchar();
      
          /*
          MyThread myth;
          myth.name = "Test name";
          myth.age = 20;
      
          thread th(&MyThread::Main, &myth);
          th.join();
          */
      
          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
    • 控制台输出

      TestXThread Main begin
      ............................
      TestXThread Main end
      
      • 1
      • 2
      • 3

    7. lambda 临时函数作为线程入口

    • lambda 函数基本格式

      • [捕捉列表] (参数) mutable -> 返回值类型 {函数体}
    • thread_lambda.cpp

      #include 
      #include 
      #include 
      
      using namespace std;
      
      class TestLambda {
      public:
          void Start() {
              thread th([this]() {cout << "name = " << name << endl; });
              th.join();
          }
          
          string name = "Test Lambda";
      };
      
      int main(int argc, char* argv[]) {
          thread th(
              [](int i) {cout << "test lambda " << i << endl;}, 
              123
          );
          th.join();
          
          TestLambda test;
          test.Start();
          
          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
    • 控制台输出

      test lambda 123
      name = Test Lambda
      
      • 1
      • 2

    8. call_once 多线程调用函数只进入一次

    初始化函数可以在每一个类型的构造里都调用一遍,不用明确区分,代码可读性提升

    • call_once.cpp

      #include 
      #include 
      #include 
      #include 
      
      using namespace std;
      
      void SystemInit() {
          cout << "Call SystemInit" << endl;
      }
      
      void CallOnceSystemInit() {
          static std::once_flag flag;  // 通过 flag 区分是否只调用一次
          std::call_once(flag, SystemInit);
      }
      
      int main(int argc, char* argv[]) {
          CallOnceSystemInit();
          CallOnceSystemInit();
          for (int i = 0; i < 3; i++) {
              thread th(CallOnceSystemInit);
              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
    • 控制台输出

      Call SystemInit
      
      • 1
  • 相关阅读:
    ps多种去水印方法与技巧-适合各种水印
    【Modbus通信实验四】实现Modbus RTU协议
    【设计模式】观察者模式及函数式编程的替代C++
    Ansible概述和模块解释(你刚走过了今天,而扑面而来的却是昨天)
    详细解释Informer模型的各部分
    [MySQL]-压力测试之Sysbench
    Springboot疫苗预约网站毕业设计-附源码190950
    1.物联网射频识别,RFID概念、组成、中间件、标准,全球物品编码——EPC码
    微信小程序注册指引
    vant3 list 的坑
  • 原文地址:https://blog.csdn.net/qq_42994487/article/details/134495093