zoukankan      html  css  js  c++  java
  • C++11 并发指南------std::thread 详解

    参考: https://github.com/forhappy/Cplusplus-Concurrency-In-Practice/blob/master/zh/chapter3-Thread/Introduction-to-Thread.md#stdthread-%E8%AF%A6%E8%A7%A3

    本节将详细介绍 std::thread 的用法。

    std::thread 在 <thread> 头文件中声明,因此使用 std::thread 需包含 <thread> 头文件。

    <thread> 头文件摘要

    <thread> 头文件声明了 std::thread 线程类及 std::swap (交换两个线程对象)辅助函数。另外命名空间 std::this_thread 也声明在 <thread> 头文件中。下面是 C++11 标准所定义的 <thread> 头文件摘要:

    参见 N3242=11-0012 草案第 30.3 节 Threads(p1133)。

    namespace std {
        #define __STDCPP_THREADS__ __cplusplus
        class thread;
        void swap(thread& x, thread& y);
        namespace this_thread {
            thread::id get_id();
            void yield();
    
            template <class Clock, class Duration>
            void sleep_until(const chrono::time_point<Clock, Duration>& abs_time);
    
            template <class Rep, class Period>
            void sleep_for(const chrono::duration<Rep, Period>& rel_time);
        }        
    }
    

    <thread> 头文件主要声明了 std::thread 类,另外在 std::this_thread 命名空间中声明了get_idyieldsleep_until 以及 sleep_for 等辅助函数,本章稍微会详细介绍 std::thread 类及相关函数。

    std::thread 类摘要

    std::thread 代表了一个线程对象,C++11 标准声明如下:

    namespace std {
        class thread {
            public:
                // 类型声明:
                class id;
                typedef implementation-defined native_handle_type;
    
                // 构造函数、拷贝构造函数和析构函数声明:
                thread() noexcept;
                template <class F, class ...Args> explicit thread(F&& f, Args&&... args);
                ~thread();
                thread(const thread&) = delete;
                thread(thread&&) noexcept;
                thread& operator=(const thread&) = delete;
                thread& operator=(thread&&) noexcept;
    
                // 成员函数声明:
                void swap(thread&) noexcept;
                bool joinable() const noexcept;
                void join();
                void detach();
                id get_id() const noexcept;
                native_handle_type native_handle();
    
                // 静态成员函数声明:
                static unsigned hardware_concurrency() noexcept;
        };
    }
    

    std::thread 中主要声明三类函数:(1). 构造函数、拷贝构造函数及析构函数;(2). 成员函数;(3). 静态成员函数。另外,std::thread::id 表示线程 ID,同时 C++11 声明如下:

    namespace std {
        class thread::id {
            public:
                id() noexcept;
        };
    
        bool operator==(thread::id x, thread::id y) noexcept;
        bool operator!=(thread::id x, thread::id y) noexcept;
        bool operator<(thread::id x, thread::id y) noexcept;
        bool operator<=(thread::id x, thread::id y) noexcept;
        bool operator>(thread::id x, thread::id y) noexcept;
        bool operator>=(thread::id x, thread::id y) noexcept;
    
        template<class charT, class traits>
        basic_ostream<charT, traits>&
            operator<< (basic_ostream<charT, traits>& out, thread::id id);
    
    
        // Hash 支持
        template <class T> struct hash;
        template <> struct hash<thread::id>;
    }
    

    std::thread 详解

    std::thread 构造和赋值

    std::thread 构造函数

    默认构造函数 (1) thread() noexcept;
    初始化构造函数 (2) template <class Fn, class... Args>
    explicit thread(Fn&& fn, Args&&... args);
    拷贝构造函数 [deleted] (3) thread(const thread&) = delete;
    Move 构造函数 (4) thread(thread&& x) noexcept;
    1. 默认构造函数(1),创建一个空的 std::thread 执行对象。
    2. 初始化构造函数(2),创建一个 std::thread 对象,该 std::thread 对象可被 joinable,新产生的线程会调用 fn 函数,该函数的参数由 args 给出。
    3. 拷贝构造函数(被禁用)(3),意味着 std::thread 对象不可拷贝构造。
    4. Move 构造函数(4),move 构造函数(move 语义是 C++11 新出现的概念,详见附录),调用成功之后 x 不代表任何std::thread 执行对象。

    注意:可被 joinable 的 std::thread 对象必须在他们销毁之前被主线程 join 或者将其设置为 detached.

    std::thread 各种构造函数例子如下(参考):

    #include <iostream>
    #include <utility>
    #include <thread>
    #include <chrono>
    #include <functional>
    #include <atomic>
    
    void f1(int n)
    {
        for (int i = 0; i < 5; ++i) {
            std::cout << "Thread " << n << " executing
    ";
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
    }
    
    void f2(int& n)
    {
        for (int i = 0; i < 5; ++i) {
            std::cout << "Thread 2 executing
    ";
            ++n;
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
    }
    
    int main()
    {
        int n = 0;
        std::thread t1; // t1 is not a thread
        std::thread t2(f1, n + 1); // pass by value
        std::thread t3(f2, std::ref(n)); // pass by reference
        std::thread t4(std::move(t3)); // t4 is now running f2(). t3 is no longer a thread
        t2.join();
        t4.join();
        std::cout << "Final value of n is " << n << '
    ';
    }
    

    std::thread 赋值操作

    Move 赋值操作 (1) thread& operator=(thread&& rhs) noexcept;
    拷贝赋值操作 [deleted] (2) thread& operator=(const thread&) = delete;
    1. Move 赋值操作(1),如果当前对象不可 joinable,需要传递一个右值引用(rhs)给 move 赋值操作;如果当前对象可被joinable,则会调用 terminate() 报错。
    2. 拷贝赋值操作(2),被禁用,因此 std::thread 对象不可拷贝赋值。

    请看下面的例子:

    #include <stdio.h>
    #include <stdlib.h>
    
    #include <chrono>    // std::chrono::seconds
    #include <iostream>  // std::cout
    #include <thread>    // std::thread, std::this_thread::sleep_for
    
    void thread_task(int n) {
        std::this_thread::sleep_for(std::chrono::seconds(n));
        std::cout << "hello thread "
            << std::this_thread::get_id()
            << " paused " << n << " seconds" << std::endl;
    }
    
    int main(int argc, const char *argv[])
    {
        std::thread threads[5];
        std::cout << "Spawning 5 threads...
    ";
        for (int i = 0; i < 5; i++) {
            threads[i] = std::thread(thread_task, i + 1);
        }
        std::cout << "Done spawning threads! Now wait for them to join
    ";
        for (auto& t: threads) {
            t.join();
        }
        std::cout << "All threads joined.
    ";
    
        return EXIT_SUCCESS;
    }
    

    其他成员函数

    本小节例子来自 http://en.cppreference.com

    • get_id: 获取线程 ID,返回一个类型为 std::thread::id 的对象。请看下面例子:

      #include <iostream>
      #include <thread>
      #include <chrono>
      
      void foo()
      {
          std::this_thread::sleep_for(std::chrono::seconds(1));
      }
      
      int main()
      {
          std::thread t1(foo);
          std::thread::id t1_id = t1.get_id();
      
          std::thread t2(foo);
          std::thread::id t2_id = t2.get_id();
      
          std::cout << "t1's id: " << t1_id << '
      ';
          std::cout << "t2's id: " << t2_id << '
      ';
      
          t1.join();
          t2.join();
      }
      
    • joinable: 检查线程是否可被 join。检查当前的线程对象是否表示了一个活动的执行线程,由默认构造函数创建的线程是不能被 join 的。另外,如果某个线程 已经执行完任务,但是没有被 join 的话,该线程依然会被认为是一个活动的执行线程,因此也是可以被 join 的。

      #include <iostream>
      #include <thread>
      #include <chrono>
      
      void foo()
      {
          std::this_thread::sleep_for(std::chrono::seconds(1));
      }
      
      int main()
      {
          std::thread t;
          std::cout << "before starting, joinable: " << t.joinable() << '
      ';
      
          t = std::thread(foo);
          std::cout << "after starting, joinable: " << t.joinable() << '
      ';
      
          t.join();
      }
      
    • join: Join 线程,调用该函数会阻塞当前线程,直到由 *this 所标示的线程执行完毕 join 才返回。

      #include <iostream>
      #include <thread>
      #include <chrono>
      
      void foo()
      {
          // simulate expensive operation
          std::this_thread::sleep_for(std::chrono::seconds(1));
      }
      
      void bar()
      {
          // simulate expensive operation
          std::this_thread::sleep_for(std::chrono::seconds(1));
      }
      
      int main()
      {
          std::cout << "starting first helper...
      ";
          std::thread helper1(foo);
      
          std::cout << "starting second helper...
      ";
          std::thread helper2(bar);
      
          std::cout << "waiting for helpers to finish..." << std::endl;
          helper1.join();
          helper2.join();
      
          std::cout << "done!
      ";
      }
      
    • detach: Detach 线程。 将当前线程对象所代表的执行实例与该线程对象分离,使得线程的执行可以单独进行。一旦线程执行完毕,它所分配的资源将会被释放。

    调用 detach 函数之后:

    1. *this 不再代表任何的线程执行实例。
    2. joinable() == false
    3. get_id() == std::thread::id()

    另外,如果出错或者 joinable() == false,则会抛出 std::system_error.

        #include <iostream>
        #include <chrono>
        #include <thread>
    
        void independentThread() 
        {
            std::cout << "Starting concurrent thread.
    ";
            std::this_thread::sleep_for(std::chrono::seconds(2));
            std::cout << "Exiting concurrent thread.
    ";
        }
    
        void threadCaller() 
        {
            std::cout << "Starting thread caller.
    ";
            std::thread t(independentThread);
            t.detach();
            std::this_thread::sleep_for(std::chrono::seconds(1));
            std::cout << "Exiting thread caller.
    ";
        }
    
        int main() 
        {
            threadCaller();
            std::this_thread::sleep_for(std::chrono::seconds(5));
        }
    
    • swap: Swap 线程,交换两个线程对象所代表的底层句柄(underlying handles)。

      #include <iostream>
      #include <thread>
      #include <chrono>
      
      void foo()
      {
          std::this_thread::sleep_for(std::chrono::seconds(1));
      }
      
      void bar()
      {
          std::this_thread::sleep_for(std::chrono::seconds(1));
      }
      
      int main()
      {
          std::thread t1(foo);
          std::thread t2(bar);
      
          std::cout << "thread 1 id: " << t1.get_id() << std::endl;
          std::cout << "thread 2 id: " << t2.get_id() << std::endl;
      
          std::swap(t1, t2);
      
          std::cout << "after std::swap(t1, t2):" << std::endl;
          std::cout << "thread 1 id: " << t1.get_id() << std::endl;
          std::cout << "thread 2 id: " << t2.get_id() << std::endl;
      
          t1.swap(t2);
      
          std::cout << "after t1.swap(t2):" << std::endl;
          std::cout << "thread 1 id: " << t1.get_id() << std::endl;
          std::cout << "thread 2 id: " << t2.get_id() << std::endl;
      
          t1.join();
          t2.join();
      }
      

    执行结果如下:

    thread 1 id: 1892
    thread 2 id: 2584
    after std::swap(t1, t2):
    thread 1 id: 2584
    thread 2 id: 1892
    after t1.swap(t2):
    thread 1 id: 1892
    thread 2 id: 2584
    
    • native_handle: 返回 native handle(由于 std::thread 的实现和操作系统相关,因此该函数返回与 std::thread 具体实现相关的线程句柄,例如在符合 Posix 标准的平台下(如 Unix/Linux)是 Pthread 库)。

      #include <thread>
      #include <iostream>
      #include <chrono>
      #include <cstring>
      #include <pthread.h>
      
      std::mutex iomutex;
      void f(int num)
      {
          std::this_thread::sleep_for(std::chrono::seconds(1));
      
         sched_param sch;
         int policy; 
         pthread_getschedparam(pthread_self(), &policy, &sch);
         std::lock_guard<std::mutex> lk(iomutex);
         std::cout << "Thread " << num << " is executing at priority "
                   << sch.sched_priority << '
      ';
      }
      
      int main()
      {
          std::thread t1(f, 1), t2(f, 2);
      
          sched_param sch;
          int policy; 
          pthread_getschedparam(t1.native_handle(), &policy, &sch);
          sch.sched_priority = 20;
          if(pthread_setschedparam(t1.native_handle(), SCHED_FIFO, &sch)) {
              std::cout << "Failed to setschedparam: " << std::strerror(errno) << '
      ';
          }
      
          t1.join();
          t2.join();
      }
      

    执行结果如下:

    Thread 2 is executing at priority 0
    Thread 1 is executing at priority 20
    
    • hardware_concurrency [static]: 检测硬件并发特性,返回当前平台的线程实现所支持的线程并发数目,但返回值仅仅只作为系统提示(hint)。

      #include <iostream>
      #include <thread>
      
      int main() {
          unsigned int n = std::thread::hardware_concurrency();
          std::cout << n << " concurrent threads are supported.
      ";
      }
      

    std::this_thread 命名空间中相关辅助函数介绍

    • get_id: 获取线程 ID。

      #include <iostream>
      #include <thread>
      #include <chrono>
      #include <mutex>
      
      std::mutex g_display_mutex;
      
      void foo()
      {
          std::thread::id this_id = std::this_thread::get_id();
      
          g_display_mutex.lock();
          std::cout << "thread " << this_id << " sleeping...
      ";
          g_display_mutex.unlock();
      
          std::this_thread::sleep_for(std::chrono::seconds(1));
      }
      
      int main()
      {
          std::thread t1(foo);
          std::thread t2(foo);
      
          t1.join();
          t2.join();
      }
      
    • yield: 当前线程放弃执行,操作系统调度另一线程继续执行。

      #include <iostream>
      #include <chrono>
      #include <thread>
      
      // "busy sleep" while suggesting that other threads run 
      // for a small amount of time
      void little_sleep(std::chrono::microseconds us)
      {
          auto start = std::chrono::high_resolution_clock::now();
          auto end = start + us;
          do {
              std::this_thread::yield();
          } while (std::chrono::high_resolution_clock::now() < end);
      }
      
      int main()
      {
          auto start = std::chrono::high_resolution_clock::now();
      
          little_sleep(std::chrono::microseconds(100));
      
          auto elapsed = std::chrono::high_resolution_clock::now() - start;
          std::cout << "waited for "
                    << std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count()
                    << " microseconds
      ";
      }
      
    • sleep_until: 线程休眠至某个指定的时刻(time point),该线程才被重新唤醒。

      template< class Clock, class Duration >
      void sleep_until( const std::chrono::time_point<Clock,Duration>& sleep_time );
      
    • sleep_for: 线程休眠某个指定的时间片(time span),该线程才被重新唤醒,不过由于线程调度等原因,实际休眠时间可能比sleep_duration 所表示的时间片更长。

      template< class Rep, class Period >
      void sleep_for( const std::chrono::duration<Rep,Period>& sleep_duration );
      
      #include <iostream>
      #include <chrono>
      #include <thread>
      
      int main()
      {
          std::cout << "Hello waiter" << std::endl;
          std::chrono::milliseconds dura( 2000 );
          std::this_thread::sleep_for( dura );
          std::cout << "Waited 2000 ms
      ";
      }
      

    执行结果如下:

    Hello waiter
    Waited 2000 ms
  • 相关阅读:
    OLAP ODS项目的总结 平台选型,架构确定
    ORACLE ORA12520
    ORACLE管道函数
    ORACLE RAC JDBC 配置
    ORACLE RAC OCFS连接产生的错误
    ORACLE 启动和关闭详解
    OLAP ODS项目的总结 起步阶段
    ORACLE RAC 配置更改IP
    ORACLE RAC OCR cann't Access
    ORACLE RAC Debug 之路 CRS0184错误与CRS初始化
  • 原文地址:https://www.cnblogs.com/ranjiewen/p/5734111.html
Copyright © 2011-2022 走看看