今回はマルチスレッドで別々の処理を同時に実行する方法について解説していきます。
別々の処理を同時に実行できれば、作成できるプログラムの幅が大きく広がります。
こういった別々の処理の同時実行は、マルチスレッドを使わなくても頑張れば実現できるかもしれませんが、マルチスレッドを使った方が簡単に実現することができます。
作成するプログラム
今回は下記の2つの処理を同時に実行するプログラムを作成したいと思います。
- 無限ループの中で変数
num
の値をprintf
で表示する - 無限ループの中で
scanf
でユーザーからの値を取得し、変数num
に格納する
動きとしては、常に printf
が num
の値を表示し続ける中、ユーザーが値を入力すれば 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
)が実行されないため、scanf
と printf
を同時に実行することができません。
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
はスレッドを新たに生成する関数になります。
1行目では output
関数を実行するスレッドが、2行目では input
関数を実行するスレッドが生成されます。
また、スレッドが生成されると、暇をしている CPU のコアがそのスレッドの処理を自動的に実行してくれます。
ご利用されているパソコンの CPU にコアが複数備えられており(最近はほとんどが複数コアを備えています)、他に処理すべきスレッド(他に起動中のアプリのスレッドなど)が無ければ、2つのスレッドがそれぞれ別のコアによって処理されますので、同時に処理が実行されることになります。
したがって、input
関数の中で scanf
で入力を待っている間も output
関数の処理が並列に同時に実行されますので、常に printf
で num
の値が表示されることになります。
また、printf
で num
の値が表示され続けている間も scanf
での入力待ちが行われていますので、数値を入力すれば num
の値が変化し、それに伴って printf
で表示される値も変化します。
コアが一つしかない CPU においても、scanf
で入力待ちしている間はコアが暇になりますので、その暇な間は printf
が実行されることになります
ですのであたかも同時に実行されているかのように処理が行われることになります
こんな感じで、スレッドを複数に分割して生成するだけで、別々の処理も同時に実行することもできるようになります。
pthread_create
などのようなスレッドを生成する関数を実行すれば良いだけですので、それなりに簡単に同時処理を実現することができます。
ちなみに下記では生成したスレッドの終了待ちを行っています。
/* スレッドの終了待ち */
pthread_join(output_thread, NULL);
pthread_join(input_thread, NULL);
pthread_join
についても下記ページで解説しているように、第1引数で指定したスレッドが終了するのを待つ関数になります。
上記により、output
関数を実行するスレッドと input
関数を実行するスレッドの処理終了を待つことになります。
もしコレを行わなければ、この2つのスレッドの終了を待つことなく main
関数が終了することになり、直ちにプログラム自体が終了してしまうことになります(生成したスレッドの処理も同時に終了させられます)。
プログラムが終了しないように、上記のように pthread_join
関数の実行を行っています。pthread_join
実行部分をコメントアウトすれば、すぐにプログラムが終了する様子も確認できて面白いと思います!
まとめ
今回はマルチスレッドにより別々の処理を同時に行う方法を、特に scanf
と printf
を同時に実行する実例を用いて解説しました。
今回解説したことは、もちろん scanf
と printf
の同時実行だけでなく、様々な処理の同時実行に応用することができます。
マルチスレッドを利用すればいろんな処理を同時に実行するプログラムが作成できるようになります。
マルチスレッドが使えるだけで作成できるプログラムの幅も一気に広がりますよー!
難しいイメージを持っている方もいるかもしれませんが、慣れれば気軽に扱えるようになります。
是非この機会にマルチスレッドプログラミングも挑戦してみてください!