C++11 线程

01 thread

thread 用于创建线程对象
thread 对象使用可调用对象进行初始化。可调用对象是线程的入口函数,初始化之后线程立即执行。
thread 初始化成功后拥有唯一的线程 ID
默认构造的(未初始化的)线程对象是不可连接(t.joinable() == false)的。

  • 成员函数

    (constructor)              
    (destructor)                
    operator=                   // 移动 thread 对象
    get_id                      // 返回线程 ID
    joinable                    // 检查是否可以合并,如果线程已经指定入口函数(会立即被执行)那么线程对象是可 join 的
    join                        // 阻塞子线程的父线程,当子线程执行完毕之后父线程继续向下执行
    detach                      // 将父线程与子线程分离
    swap                        // 交换线程
    native_handle                   // 返回底层定义实现的线程句柄
    hardware_concurrency [static]   // 返会当前硬件实际支持的并发线程数
    
  • 非成员函数

    std::swap();                // 特化 std::swap 算法
    
  • 注意事项

    • 线程对象不能被复制,可以被移动。

    • 不能调用已经结束线程对象的 join() 方法,因此在使用 join() 之前最后使用 joinable() 检查线程可否被 join().

    • 线程是否可被 join 的判别规则:

      • 线程对象代表一个执行线程(只要初始化制定了入口函数),则它是可 join 的。
      • 线程对象是默认构造的已被移动已经被 join 或 detach 的线程对象都不可被 join.
    • 一般情况下主线程结束之后操作系统会强制关闭其子线程,如果此时子线程没有执行完毕可能会产生错误,因此一般要保证主线程退出之前子线程执行执行完毕(使用 t.join()),但是如果不关系子线程的执行情况,可以使用 detach() 方法将其与父线程进行分离,分离后的子线程与元父线程没有任何关系,也就不能再在父线程中调用 join()。

    • 向线程传递参数时传递方式是值传递,如果想要参数按照引用方式传递到线程中需要使用 std::ref(obj).

      void func01() 
      {
          std::cout << "sub  thread: hello main thread." << std::endl;
      }
      
      void func02(int m, int n)
      {
          m *= 2;
          std::cout << "sub  thread: " << m << " " << n << std::endl;
      }
      
      // 接收父线程显式传递过来的引用参数需要入口函数设置相应的形参类型
      void func03(int &m, int n)
      {
          m *= 2;
          std::cout << "sub  thread: " << m << " " << n << std::endl;
      }
      
      int n01 = 10, n02 = 15;
      
      std::thread t07(func01);
      t07.join();
      // 默认传递到线程中的参数传递方式为值传递
      std::thread t08(func02, n01, n02);
      t08.join();
      std::cout << "main thread: " << n01 << " " << n02 << std::endl;
      // 显式使用引用传递参数到线程运行函数中
      std::thread t09(func03, std::ref(n01), n02);
      t09.join();
      std::cout << "main thread: " << n01 << " " << n02 << std::endl;
      
      /*
      // ---------------------------------------------------------
      // output
      // ---------------------------------------------------------
      sub  thread: hello main thread.
      sub  thread: 20 15
      main thread: 10 15
      sub  thread: 20 15
      main thread: 20 15
      */
      
    • 传递对象做线程参数需要注意以下的情况:

      • 使用 detach() 时如果主线程先结束,变量就会被回收,之后子线程对这些变量的操作将是未定义的,因此使用 detach() 时不推荐使用引用和指针
    • hardware_concurrency 返回的硬件支持的线程数量只是一个近似值。如果此值不可计算或者定义不明确则返回 0。

  • 应用实例

    /***************************************************************
     * (constructor)
     * operator= 
     * ************************************************************/
    // -------------------------------------------------------------
    // 默认构造
    // -------------------------------------------------------------
    std::thread t01;
    
    // -------------------------------------------------------------
    // 使用可调用对象构造
    // -------------------------------------------------------------
    void CallableObject01() {}
    
    struct CallableObject02
    {
        void operator()() {}
    };
    
    struct CallableObject03
    {
        using func_t = void(*)();
    
        static void func() {}
    
        operator func_t() { return func; }
    };
    
    struct CallableObject04
    {
        void func() {}
    };
    
    // 01 函数指针
    auto co01 = CallableObject01;       // auto = void (*co01)()
    std::thread t02(co01);
    
    // 02 重载 operator() 运算符的结构体/类对象
    CallableObject02 co02;
    std::thread t03(co02);
    
    // 03 可以被转换为函数指针的结构体/类对象
    CallableObject03 co03;
    std::thread t04(co03);
    
    // 04 结构体/类成员函数指针
    CallableObject04 co04;
    std::thread t05(&CallableObject04::func, co04);
    
    // 05 Lambda 表达式
    std::thread t06([]() {});
    
    // -------------------------------------------------------------
    // 传递实参到线程中
    // -------------------------------------------------------------
    void func01() 
    {
        std::cout << "hello thread" << std::endl;
    }
    
    void func02(int m, int n)
    {
        m *= 2;
        std::cout << m << " " << n << std::endl;
    }
    
    // 接收父线程显式传递过来的引用参数需要入口函数设置相应的形参类型
    void func03(int &m, int n)
    {
        m *= 2;
        std::cout << "sub  thread: " << m << " " << n << std::endl;
    }
    
    int n01 = 10, n02 = 15;
    
    std::thread t07(func01);
    t07.join();
    // 默认传递到线程中的参数传递方式为值传递
    std::thread t08(func02, n01, n02);
    t08.join();
    std::cout << "main thread: " << n01 << " " << n02 << std::endl;
    // 显式使用引用传递参数到线程运行函数中
    std::thread t09(func03, std::ref(n01), n02);
    t09.join();
    std::cout << "main thread: " << n01 << " " << n02 << std::endl;
    
    /*
    // ---------------------------------------------------------
    // output
    // ---------------------------------------------------------
    sub  thread: hello main thread.
    sub  thread: 20 15
    main thread: 10 15
    sub  thread: 20 15
    main thread: 20 15
    */
    
    // 拷贝行为(拷贝构造、拷贝赋值运算符重载)被禁止
    // 允许使用移动行为(移动构造、移动赋值运算符重载)
    void func01() 
    {
        std::cout << "sub  thread: hello main thread." << std::endl;
    }
    
    std::thread t01(func01);
    std::thread t02(t01);                       // error
    std::thread t03 = t01;                      // error
    std::thread t04(std::move(t01));            // ok
    std::thread t05 = std::move(t04);           // ok
    
    /***************************************************************
     * get_id
     * ************************************************************/
    void func01() {
        // 设置当前线程休眠 10s
        std::this_thread::sleep_for(std::chrono::seconds(10));
    }
    
    std::thread t01;
    std::thread t02(func01);
    
    // auto = std::thread::id 
    auto mainThreadId = std::this_thread::get_id();
    std::cout << mainThreadId << std::endl;     // 44420[非固定值]
    // 线程不能被 join 时将返回一个默认的线程 id(id = 0)
    std::cout << t01.get_id() << std::endl;     // 0
    std::cout << t02.get_id() << std::endl;     // 11512[非固定值]
    
    // t01.join();                              // error: 没有设置入口函数的线程不能被 join
    t02.join(); 
    
    /***************************************************************
     * joinable
     * join
     * detach
     * ************************************************************/
    void func01(int sleepTime) {
        std::this_thread::sleep_for(std::chrono::seconds(sleepTime));
    }
    
    void checkJoinable(std::thread& th, const int &num)
    {
        if (th.joinable())
        {
            th.join();
            std::cout << "thread0" << num << " is joinable." << std::endl;
        }
    }
    
    std::thread t01;
    std::thread t02(func01, 0);
    std::thread t03(func01, 10);
    std::thread t04(func01, 10);
    std::thread t05(func01, 10);
    std::thread t06(std::move(t03));
    
    t04.detach();
    t05.join();
    
    // 主线程等待 5s 让线程 t02 执行完毕
    std::this_thread::sleep_for(std::chrono::seconds(5));
    
    checkJoinable(t01, 1);      // 默认构造的线程对象不能被 join
    checkJoinable(t02, 2);      // 执行完毕的线程是可以被 join 
    checkJoinable(t03, 3);      // 被移动的线程不能被 join
    checkJoinable(t04, 4);      // 被 detach 的线程不能被 join  
    checkJoinable(t05, 5);      // 被 join 的线程不能再次被 join
    checkJoinable(t06, 6);      // 正在执行的线程可被正常 join
    /*
    // ---------------------------------------------------------
    // output
    // ---------------------------------------------------------
    thread02 is joinable.
    thread06 is joinable.
    */
    
    /***************************************************************
     * swap
     * ************************************************************/
    void func01() {}
    
    std::thread t01(func01);
    std::thread t02(func01);
    
    std::cout << t01.get_id() << std::endl;     // 26040
    std::cout << t02.get_id() << std::endl;     // 36984
    
    t01.swap(t02);
    
    std::cout << t01.get_id() << std::endl;     // 36984
    std::cout << t02.get_id() << std::endl;     // 26040
    
    /***************************************************************
     * native_handle
     * ************************************************************/
    void func01() {}
    
    std::thread t01(func01);
    
    std::cout << t01.get_id() << std::endl;     // 18960
    
    auto thHandle = t01.native_handle();    
    
    std::cout << thHandle << std::endl;         // 0000000000000094
    
    t01.join();
    
    /***************************************************************
     * hardware_concurrency [static]
     * ************************************************************/
    std::cout << std::thread::hardware_concurrency() << std::endl; // 8
    

02 this_thread

this_thread 是一个命名空间,提供了访问当前执行线程的方法

  • 函数

    get_id                      // 获取线程 id
    yield                       // 建议实现重新调度各执行线程
    sleep_for                   // 休眠指定时间
    sleep_until                 // 休眠直到到达指定时间
    
  • 注意事项

    • yield 的作用是当前线程放弃执行,让出时间片令 CPU 可以执行其它线程。可以用于在主线程中设置开始子线程的标识,在此标识设置为 true 之前,子线程将不会被执行(默认情况下线程对象在被初始化之后立即执行)。
  • 函数实例

    /***************************************************************
     * get_id
     * ************************************************************/
    std::cout << std::this_thread::get_id() << std::endl;   // 44420
    
    /***************************************************************
     * yield
     * sleep_for
     * ************************************************************/
    #include <atomic>
    #include <chrono>
    
    std::atomic<bool> runFlag(false);
    
    void func01() {
        // 线程已经完成初始化
        std::cout << "thread is ready." << std::endl;
    
        while (!runFlag)
        {
            // 还没有收到开始的指令
            std::this_thread::yield();
        }
    
        std::this_thread::sleep_for(std::chrono::seconds(1));
    
        std::cout << "thread is running." << std::endl;
    
        // ...
    
        std::cout << "thread is done" << std::endl;
    }
    
    std::thread t01(func01);
    
    std::this_thread::sleep_for(std::chrono::seconds(5));
    
    runFlag = true;
    
    std::cout << "go go go." << std::endl;
    
    t01.join();
    
    /*
    // ---------------------------------------------------------
    // output
    // ---------------------------------------------------------
    thread is ready.
    go go go.
    thread is running.
    thread is done
    */
    
    /***************************************************************
     * sleep_until
     * ************************************************************/
    #define _CRT_SECURE_NO_WARNINGS
    // 解决强制使用安全版本函数的方法 <https://blog.csdn.net/dan15188387481/article/details/49622783>
    
    #include <chrono>
    #include <ctime>
    #include <iomanip>
    // ...
    
    int main()
    {
        using std::chrono::system_clock;
        std::time_t tt = system_clock::to_time_t(system_clock::now());
    
        struct std::tm *ptm = std::localtime(&tt);
        std::cout << std::put_time(ptm, "%X") << " now" << std::endl;
    
        ++ptm->tm_min;
        std::this_thread::sleep_until(system_clock::from_time_t(mktime(ptm)));
        std::cout << "sleep until " << std::put_time(ptm, "%X") << std::endl;
    
        std::cout << std::put_time(ptm, "%X") << " reached" << std::endl;
    }
    
    /*
    // ---------------------------------------------------------
    // output
    // ---------------------------------------------------------
    23:30:05 now
    sleep until 23:31:05
    23:31:05 reached
    */
    

C++11 多线程并发编程目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值