Rainbow Engine

IT技術を分かりやすく簡潔にまとめることによる学習の効率化、また日常の気付きを記録に残すことを目指します。

C++

C++のマルチスレッド処理のサンプルや概要について

投稿日:2021年9月26日 更新日:

 

<目次>

(1) C++のマルチスレッド処理のサンプルや概要について
 (1-1) C++のマルチスレッドの概要
 (1-2) 構文1:関数ポインタ式
 (1-3) 構文1(例):関数ポインタ式(シングル)
 (1-4) 構文1(例):関数ポインタ式(マルチ)
 (1-5) 構文2:関数オブジェクト式
 (1-6) 構文2(例):関数オブジェクト式(シングル)
 (1-7) エラー対処

(1) C++のマルチスレッド処理のサンプルや概要について

(1-1) C++のマルチスレッドの概要

マルチスレッド化はC++11以降に導入されました。C++11以前はPOSIXスレッドやPスレッドライブラリを使用する必要がありましたが、11以降は「std::thread」(std名前空間のthreadクラス)が使えるようになりました。
 
「std::thread」は単一のスレッドを表現しており、スレッドを開始するにはシンプルに、スレッドクラスのコンストラクタを使ってオブジェクトを生成します。その際にコンストラクタの引数にCallable(※注1)等の実行対象のコードを渡す事で、スレッドが開始されます。
 
(※注1)Callableの具体例は以下
①関数へのポインタ等
②関数オブジェクト(=Functor=関数のように振る舞うオブジェクト)
 

(1-2) 構文1:関数ポインタ式

「std::thread スレッド名(・・・)」のカッコの中に、スレッド実行する対象の処理(関数ポインタ)と、カンマの後にその引数を与えます。
 
(構文)スレッドの生成
std::thread スレッド名(関数名, 引数1, 引数2, ・・・);
 
また、スレッド処理が開始されたら、次のアクションを実行する前に、スレッド処理の完了を待つ必要があります(例:スレッドに画面の初期化を依頼した場合、画面を正しくロードするためにはスレッド処理の完了を待つ必要あり)。
 
スレッドの完了を待つには「std::thread::join()」関数を使います。
 
(構文)スレッドの完了待ち
スレッド名.join();
 
これにより、例えば発行したスレッドの処理が完了するまで、mainスレッドを処理しないといった制御が可能になります。
 

(1-3) 構文1(例):関数ポインタ式(シングル)

上記構文を使った「シングルスレッド」の例です。
 
(例)
#include <iostream>
#include <thread>
using namespace std;

//# スレッドの中で呼び出す関数
void hoge(int Z)
{
    cout << Z << endl;
}

int main()
{
    //# スレッド起動(関数ポインタをCallableとして起動)
    thread thr1(hoge, 999);
    //# th1の完了を待つ
    thr1.join();   
    return 0;
}
 
(図121)

●コンパイル&実行

# コンパイル
$ g++ -g -o ./[ご自身のプログラム名] ./[ご自身のプログラム名].cpp -pthread -std=c++11
# 実行
$ ./[ご自身のプログラム名]

(図122)

目次にもどる

(1-4) 構文1(例):関数ポインタ式(マルチ)

上記構文を使った「マルチスレッド」の例です。こちらは単に先ほど1回だったスレッド起動を3回にして、3スレッドを同時実行しています。処理の出力順番はPCのスペックや実行した時の状態で変動します(必ずしも、thr1⇒thr2⇒thr3の順番で実行されるとは限らない)
 
(例)
#include <iostream>
#include <thread>
using namespace std;

//# スレッドの中で呼び出す関数
void hoge(int Z, int thrNo)
{
    cout<<"### THREAD#"<<thrNo<<" START : ";
    for(int i=0; i<Z; i++){
        cout << i << " ";
    }
    cout<<endl;
}

int main()
{
    //# スレッド起動(関数ポインタをCallableとして起動)
    thread thr1(hoge, 10, 1);
    thread thr2(hoge, 20, 2);
    thread thr3(hoge, 30, 3);
    //# thNの完了を待つ
    thr1.join();   
    thr2.join(); 
    thr3.join(); 
    return 0;
}
(図131)

●コンパイル&実行

# コンパイル
$ g++ -g -o ./[ご自身のプログラム名] ./[ご自身のプログラム名].cpp -pthread -std=c++11
# 実行
$ ./[ご自身のプログラム名]

↓(図132)では、プログラムの指示としてはスレッド1で「0~9」を、スレッド2で「0~19」を、スレッド3で「0~29」を、それぞれ表示する処理ですが、上述の通り必ずしもthr1⇒thr2⇒thr3の順番で実行されるとは限らないため、表示が入り組んでおり、加えて実行する度に表示が変化しています。

(図132)

目次にもどる

(1-5) 構文2:関数オブジェクト式

「std::thread スレッド名(・・・)」のカッコの中に、スレッド実行する対象の処理(関数オブジェクト)と、カンマの後にその引数を与えます。
 
(構文)スレッドの生成
std::thread スレッド名(関数オブジェクト名, 引数1, 引数2, ・・・);

(1-6) 構文2(例):関数オブジェクト式(シングル)

上記構文を使った「シングルスレッド」の例です。
(例)
#include <iostream>
#include <thread>
using namespace std;

//# スレッドの中で呼び出す関数
// A callable object
class thr_obj {
public:
    void operator()(int Z,int thrNo)
    {
        cout<<"### THREAD#"<<thrNo<<" START : ";
        for (int i = 0; i < Z; i++){
            cout << i << " ";
        }
        cout<<endl;
    }
};

int main()
{
    //# スレッド起動(関数ポインタをCallableとして起動)
    thread thr2(thr_obj(), 20, 2);
    //# thNの完了を待つ
    thr2.join(); 
    return 0;
}
(図141)

●コンパイル&実行

# コンパイル
$ g++ -g -o ./[ご自身のプログラム名] ./[ご自身のプログラム名].cpp -pthread -std=c++11
# 実行
$ ./[ご自身のプログラム名]

(図142)

目次にもどる

(1-7) エラー処理

●エラー(実行時)

terminate called after throwing an instance of 'std::system_error'
  what():  Enable multithreading to use std::thread: Operation not permitted
Aborted (core dumped)

(図151)

●対処
コンパイル時のコマンドに「-pthread」を追記して、pthreadをリンクさせます。

# Before
g++ -g -o ./thread_single1 ./thread_single1.cpp -std=c++11
# After
g++ -g -o ./thread_single1 ./thread_single1.cpp -pthread -std=c++11

(図152)

目次にもどる

Adsense審査用広告コード


Adsense審査用広告コード


-C++

執筆者:


comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

関連記事

C++の構造体とは?の作り方や使い方について

  <目次> (1) C++の構造体とは?の作り方や使い方について  (1-1) 構造体とは?  (1-2) サンプルプログラム  (1-3) 構造体の変数を定義する方法  (1-4) 構造 …

C++のポインタとは?概念や基本的な使い方をご紹介

  <目次> (1) C++のポインタとは?概念や基本的な使い方をご紹介  (1-1) はじめに  (1-2) ポインタとは? (1) C++のポインタとは?概念や基本的な使い方をご紹介 「 …

GDBの使い方をC++のプログラムのデバッグを例にご紹介

  <目次> (1) GDBの使い方をC++のプログラムのデバッグを例にご紹介  (1-1) GDBのデバッグのシナリオ概要(例)  (1-2) GDBのデバッグ手順(例)  (1-3) そ …

C++の動的メモリ割当と静的メモリ割当の違いについて

  <目次> (1) C++の動的メモリ割当と静的メモリ割当の違いについて  (1-1) 静的メモリ割当てとは?  (1-2) 動的メモリとは?  (1-3) 動的メモリの割当て  (1-4 …

C++の「int **g = new int*;」の意味について

  <目次> (1) C++の「int **g = new int*;」の意味について  (1-1) 「int **g = new int*;」の意味  (1-2) サンプルで理解を深める …

  • English (United States)
  • 日本語
Top