std::thread

16458 ワード

この記事は以下のとおりです.https://github.com/forhappy/Cplusplus-Concurrency-In-Practice/blob/master/zh/chapter3-Thread/Introduction-to-Thread.md
このセクションでは、  std::thread  を選択します.std::thread  にある   ヘッダファイルに宣言されているため、  std::thread  含める必要がある   ヘッダファイル.  ヘッダファイルの概要  ヘッダファイルはstd::threadスレッドクラスと  std::swap  (2つのスレッドオブジェクトを交換する)補助関数.別名空間  std::this_thread  また、   ヘッダファイルにあります.以下はC++11規格で定義されています   ヘッダファイルの概要:
N 3242=11-0012ドラフト第30.3節Threads(p 1133)を参照.
namespace std {
    #define __STDCPP_THREADS__ __cplusplus
    class thread;
    void swap(thread& x, thread& y);
    namespace this_thread {
        thread::id get_id();
        void yield();

        template 
        void sleep_until(const chrono::time_point& abs_time);

        template 
        void sleep_for(const chrono::duration& rel_time);
    }        
}
 ヘッダファイルは主に宣言されています  std::thread  クラス  std::this_thread  ネーミングスペースに宣言されました  get_id , yield , sleep_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  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  の中で主に3種類の関数を宣言します:(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;

    template
    basic_ostream&
        operator<< (basic_ostream& out, thread::id id);


    // Hash   
    template  struct hash;
    template <> struct hash<:id>;
}
std::thread  詳しく説明するstd::thread  構築と割り当てstd::thread  コンストラクタ
デフォルトコンストラクション関数(1)
thread() noexcept;
初期化コンストラクション関数(2)
template explicit thread(Fn&& fn, Args&&... args);
コピーコンストラクタ[deleted](3)
thread(const thread&) = delete;
Moveコンストラクション関数(4)
thread(thread&& x) noexcept;
  • デフォルトのコンストラクション関数(1)は、空の  std::thread  オブジェクトを実行します.
  • 構造関数(2)を初期化し、  std::thread  オブジェクト、  std::thread  オブジェクト  joinable、新しく生成されたスレッドが呼び出されます.  fn  関数、この関数のパラメータは  args  与える.
  • コピーコンストラクタ(無効化)(3)は、  std::thread  オブジェクトのコピー不可構造.
  • Moveコンストラクタ(4)、moveコンストラクタ(moveの意味はC++11の新しい概念で、詳細は付録を参照)、呼び出しに成功した後  x  何の意味もない  std::thread  オブジェクトを実行します.

  • 注意:  joinable  の  std::thread  オブジェクトは破棄される前にプライマリスレッドによって破棄される必要があります  join  または  detached .
    std::threadの各種構造関数の例は以下の通りである(参考):
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    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;
  • Move割付操作(1)現在のオブジェクトができない場合  joinable、右の値の参照を渡す必要があります(rhs).  move  割り当て操作;現在のオブジェクトが  joinableが呼び出されます  terminate()エラーが発生しました.
  • コピー付与操作(2)が無効になっているため、  std::thread  オブジェクトはコピーできません.

  • 次の例を見てください.
    #include 
    #include 
    
    #include     // std::chrono::seconds
    #include   // std::cout
    #include     // 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 
      #include 
      #include 
       
      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 
      #include 
      #include 
       
      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 
      #include 
      #include 
       
      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関数を呼び出した後:
  • *this  スレッド実行インスタンスを表すものではありません.
  • joinable() == false
  • get_id() == std::id()

  • また、エラーや  joinable() == falseは、  std::system_error .
        #include 
        #include 
        #include 
         
        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スレッドは、2つのスレッドオブジェクトが表す最下位ハンドル(underlying handles)を交換します.
      #include 
      #include 
      #include 
       
      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  具体的には、関連するスレッドハンドルが実装され、例えば、Unix/LinuxのようなPosix規格に適合するプラットフォームの下でPthreadライブラリである.
      #include 
      #include 
      #include 
      #include 
      #include 
       
      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<: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 
      #include 
       
      int main() {
          unsigned int n = std::thread::hardware_concurrency();
          std::cout << n << " concurrent threads are supported.
    "; }
  • std::this_thread  ネーミングスペースの関連補助関数の説明
  • get_id:スレッドIDを取得します.
      #include 
      #include 
      #include 
      #include 
       
      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 
      #include 
      #include 
       
      // "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<:chrono::microseconds>(elapsed).count()
                    << " microseconds
    "; }
  • sleep_until:スレッドは指定された時刻(time point)までスリープし、スレッドが再起動されます.
      template< class Clock, class Duration >
      void sleep_until( const std::chrono::time_point& sleep_time );
    
  • sleep_for:スレッドが指定されたタイムスライス(time span)をスリープし、そのスレッドが再起動されますが、スレッドスケジューリングなどの理由で、実際のスリープ時間は  sleep_duration  表示されるタイムスライスはもっと長いです.
      template< class Rep, class Period >
      void sleep_for( const std::chrono::duration& sleep_duration );
    
      #include 
      #include 
      #include 
       
      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

     
    参考記事:C++の同時およびマルチスレッド