【C言語】マルチスレッドで別々の処理を同時に実行する

マルチスレッドで別々の処理を同時に実行する方法解説ページアイキャッチ

今回はマルチスレッドで別々の処理を同時に実行する方法について解説していきます。

別々の処理を同時に実行できれば、作成できるプログラムの幅が大きく広がります。

こういった別々の処理の同時実行は、マルチスレッドを使わなくても頑張れば実現できるかもしれませんが、マルチスレッドを使った方が簡単に実現することができます。

作成するプログラム

今回は下記の2つの処理を同時に実行するプログラムを作成したいと思います。

  • 無限ループの中で変数 num の値を printf で表示する
  • 無限ループの中で scanf でユーザーからの値を取得し、変数 num に格納する

動きとしては、常に printfnum の値を表示し続ける中、ユーザーが値を入力すれば num の値が変わり、printf で表示される値も変化するというものになります。

なんだか簡単そうに思えるかもしれませんが、これはマルチスレッドを利用しないと実現するのはかなり難しいです。

シングルスレッドでのプログラム(NG例)

例えば下記のようなプログラムはどうでしょう?

シングルスレッド
#include <stdio.h>

int main(void) {
    int num = 0;

    for (;;) {
        scanf("%d", &num);
        printf("num = %d\n", num);
        if (num == -1) {
            break;
        }
    }

    return 0;
}

このプログラムだと、scanf を行っている間は、それ以降に記述した処理(printf)が実行されないため、scanfprintf を同時に実行することができません。

scanf はユーザーからの入力が無ければ、その間関数の中で待ち続ける関数になりますので、この間 printf で値を表示し続けることができません。

こういった「入力待ち」と他の処理の同時実行は、マルチスレッドを利用すれば簡単に実現することができます。

スポンサーリンク

マルチスレッドでのプログラム

ではそのマルチスレッドを利用したプログラムを作成していきたいと思います。

プログラムは基本的に上側に記述されている処理から逐次的に実行されていきます。

が、マルチスレッドを利用すれば複数のスレッド(処理・仕事)を同時に(並列に)実行することができます。

下記ページで初心者向け解説を行っていますので、マルチスレッドの基礎を学びたい方は是非こちらも読んでみてください。

徹底図解!入門者向け!C言語でのマルチスレッドをわかりやすく解説

スレッド設計

まずはマルチスレッド処理を行うために、スレッドの分割を行っていきます。

下記ページで解説しているように、スレッドとは「仕事」のことで、マルチスレッドとは「複数に分割された仕事」です。

徹底図解!入門者向け!C言語でのマルチスレッドをわかりやすく解説

今回は作成したいのは下記の2つの処理を同時に実行するプログラムです。

  • 無限ループの中で変数 num の値を printf で表示する
  • 無限ループの中で scanf でユーザーからの値を取得し、変数 num に格納する

ですので、1つ目のスレッドとしては、

  • 無限ループの中で変数 num の値を printf で表示する(output 関数)

2つ目のスレッドとしては、

  • 無限ループの中で scanf でユーザーからの値を取得し、変数 num に格納する(input 関数)

にスレッドを分割したいと思います。

スレッドを分割することで、分割されたスレッドが CPU によって並列に実行させることができます。

コレを利用して、マルチスレッド処理により上記のプログラムを実現していきたいと思います。

プログラム

前述のようにスレッドを分割した際のプログラムは下記のようになります(紹介するプログラムでは pthread を利用していますので、pthread が利用できる環境で実行する必要があります)。

マルチスレッド
#include <stdio.h>
#include <pthread.h>

int num = 0;

void* output(void *arg){

    for(;;){
        if (num == -1) {
            break;
        }
        printf("num = %d\n", num);
    }

    return NULL;
}

void* input(void *arg){

    for(;;){
        scanf("%d", &num);
        if (num == -1) {
            break;
        }
    }

    return NULL;
}

int main(void){
    pthread_t output_thread;
    pthread_t input_thread;

    /* スレッド作成 */
    pthread_create(&output_thread, NULL, output, NULL);
    pthread_create(&input_thread, NULL, input, NULL);

    /* スレッドの終了待ち */
    pthread_join(output_thread, NULL);
    pthread_join(input_thread, NULL);
    
    return 0;
}

pthread を利用していますので、リンク(コンパイル)時に -lpthread オプションを付けて実行してください。

スポンサーリンク

プログラムの解説

プログラムの下記部分でスレッドの生成を行っています。

スレッドの生成
/* スレッド作成 */
pthread_create(&output_thread, NULL, output, NULL);
pthread_create(&input_thread, NULL, input, NULL);

下記ページで解説しているように(簡単にですが…)、pthread_create はスレッドを新たに生成する関数になります。

徹底図解!入門者向け!C言語でのマルチスレッドをわかりやすく解説

1行目では output 関数を実行するスレッドが、2行目では input 関数を実行するスレッドが生成されます。

また、スレッドが生成されると、暇をしている CPU のコアがそのスレッドの処理を自動的に実行してくれます。

ご利用されているパソコンの CPU にコアが複数備えられており(最近はほとんどが複数コアを備えています)、他に処理すべきスレッド(他に起動中のアプリのスレッドなど)が無ければ、2つのスレッドがそれぞれ別のコアによって処理されますので、同時に処理が実行されることになります。

したがって、input 関数の中で scanf で入力を待っている間も output 関数の処理が並列に同時に実行されますので、常に printfnum の値が表示されることになります。

また、printfnum の値が表示され続けている間も scanf での入力待ちが行われていますので、数値を入力すれば num の値が変化し、それに伴って printf で表示される値も変化します。

MEMO

コアが一つしかない CPU においても、scanf で入力待ちしている間はコアが暇になりますので、その暇な間は printf が実行されることになります

ですのであたかも同時に実行されているかのように処理が行われることになります

こんな感じで、スレッドを複数に分割して生成するだけで、別々の処理も同時に実行することもできるようになります。

pthread_create などのようなスレッドを生成する関数を実行すれば良いだけですので、それなりに簡単に同時処理を実現することができます。

ちなみに下記では生成したスレッドの終了待ちを行っています。

スレッドの終了待ち
/* スレッドの終了待ち */
pthread_join(output_thread, NULL);
pthread_join(input_thread, NULL);

pthread_join についても下記ページで解説しているように、第1引数で指定したスレッドが終了するのを待つ関数になります。

徹底図解!入門者向け!C言語でのマルチスレッドをわかりやすく解説

上記により、output 関数を実行するスレッドと input 関数を実行するスレッドの処理終了を待つことになります。

もしコレを行わなければ、この2つのスレッドの終了を待つことなく main 関数が終了することになり、直ちにプログラム自体が終了してしまうことになります(生成したスレッドの処理も同時に終了させられます)。

プログラムが終了しないように、上記のように pthread_join 関数の実行を行っています。pthread_join 実行部分をコメントアウトすれば、すぐにプログラムが終了する様子も確認できて面白いと思います!

まとめ

今回はマルチスレッドにより別々の処理を同時に行う方法を、特に scanfprintf を同時に実行する実例を用いて解説しました。

今回解説したことは、もちろん scanfprintf の同時実行だけでなく、様々な処理の同時実行に応用することができます。

マルチスレッドを利用すればいろんな処理を同時に実行するプログラムが作成できるようになります。

マルチスレッドが使えるだけで作成できるプログラムの幅も一気に広がりますよー!

難しいイメージを持っている方もいるかもしれませんが、慣れれば気軽に扱えるようになります。

是非この機会にマルチスレッドプログラミングも挑戦してみてください!

コメントを残す

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