C で Linux スレッドを作成する方法

C で Linux スレッドを作成する方法

Linux では、POSIX スレッド (pthread) ライブラリを使用して C/C++ でスレッドを作成および管理できます。他のオペレーティング システムとは異なり、Linux ではスレッドとプロセスの間にほとんど違いはありません。そのため、Linux はしばしばそのスレッドを軽量プロセスと呼んでいます。

pthread ライブラリを使用すると、スレッドを作成し、それらが終了するのを待って、明示的に終了することができます。

Linux でのスレッド使用の履歴

Linux バージョン 2.6 より前では、メイン スレッドの実装は LinuxThreads でした。この実装には、パフォーマンスと同期操作に関して大きな制限がありました。実行できるスレッドの最大数の制限により、1000 に制限されていました。

2003 年、IBM と RedHat の開発者が率いるチームが、Native POSIX Thread Library (NPTL) プロジェクトを利用可能にすることに成功しました。Linux 上の Java 仮想マシンのパフォーマンスの問題を解決するために、RedHat Enterprise バージョン 3 で初めて導入されました。現在、GNU C ライブラリには両方のスレッド化メカニズムの実装が含まれています。

これらはどちらも、仮想マシンが純粋なユーザー モードで管理および実行するグ​​リーン スレッドの実装ではありません。pthread ライブラリを使用すると、カーネルはプログラムが開始するたびにスレッドを作成します。

/proc/<PID>/taskの下のファイルで、実行中のプロセスに関するスレッド固有の情報を見つけることができます。これは、procfs Linux 標準の下でのプロセス情報の標準的な場所です。シングルスレッド アプリケーションの場合、このディレクトリの下に PID と同じ値を持つタスク レコードがあるように見えます。

スレッドの動作ロジック

スレッドは、オペレーティング システムで現在実行されているプロセスのようなものです。シングル プロセッサ システム (マイクロコントローラなど) では、オペレーティング システムのカーネルがスレッドをシミュレートします。これにより、スライスによってトランザクションを同時に実行できます。

シングルコア オペレーティング システムは、実際には一度に 1 つのプロセスしか実行できません。ただし、マルチコアまたはマルチプロセッサ システムでは、これらのプロセスを同時に実行できます。

C でのスレッドの作成

pthread_create関数を使用して、新しいスレッドを作成できます。pthread.hヘッダー ファイルには、他のスレッド関連の関数と共に署名定義が含まれていますスレッドは、メイン プログラムと同じアドレス空間とファイル記述子を使用します。

pthread ライブラリには、同期操作に必要なミューテックスおよび条件付き操作に必要なサポートも含まれています。

pthread ライブラリの関数を使用している場合は、コンパイラがpthreadライブラリを実行可能ファイルにリンクしていることを確認する必要があります。必要に応じて、-lオプションを使用してライブラリにリンクするようにコンパイラに指示できます。

gcc -o test test_thread.c -lpthread

pthread_create 関数には、次の署名があります。

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)

手順が成功した場合は 0 を返します。問題がある場合は、ゼロ以外のエラー コードを返します。上記の関数シグネチャでは:

  • スレッドパラメータの型はpthread_tです。作成されたスレッドは、この参照で常にアクセスできます。
  • attrパラメータを使用すると、カスタム動作を指定できます。pthread_attr_で始まる一連のスレッド固有の関数を使用して、この値を設定できます。可能なカスタマイズは、スケジューリング ポリシー、スタック サイズ、デタッチ ポリシーです。
  • start_routineは、スレッドが実行する関数を指定します。
  • argは、スレッドによって関数に渡される汎用データ構造を表します。

アプリケーションの例を次に示します。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

void *worker(void *data)
{
char *name = (char*)data;

for (int i = 0; i < 120; i++)
{
usleep(50000);
printf("Hi from thread name = %s\n", name);
}

printf("Thread %s done!\n", name);
return NULL;
}

int main(void)
{
pthread_t th1, th2;
pthread_create(&th1, NULL, worker, "X");
pthread_create(&th2, NULL, worker, "Y");
sleep(5);
printf("Exiting from main program\n");
return 0;
}

同時に実行されている 2 つのスレッドを示すプログラムからの出力

ねじの種類

スレッドがアプリケーションのmain()関数から戻ると、すべてのスレッドが終了し、システムはプログラムが使用していたすべてのリソースを解放します。同様に、exit()のようなコマンドでスレッドを終了すると、プログラムはすべてのスレッドを終了します。

pthread_join関数を使用すると、代わりにスレッドが終了するのを待つことができます。この関数を使用するスレッドは、予想されるスレッドが終了するまでブロックされます。システムから使用されるリソースは、結合可能なスレッドの終了、CPU によってスケジュールされていない、またはptread_joinでの結合に失敗した場合でも返されません。

pthread_join での結合が意味をなさない場合があります。たとえば、スレッドがいつ終了するかを予測できない場合。この場合、スレッドが戻った時点で、システムがすべてのリソースを自動的に返すようにすることができます。

これを実現するには、関連するスレッドをDETACHEDステータスで開始する必要があります。スレッドの開始時に、スレッド属性値またはpthread_detach関数を使用してDETACHステータスを設定できます。

int pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
int pthread_detach(pthread_t thread);

pthread_join() の使用例を次に示します。最初のプログラムの main 関数を次のように置き換えます。

int main(void)
{
pthread_t th1, th2;
pthread_create(&th1, NULL, worker, "X");
pthread_create(&th2, NULL, worker, "Y");
sleep(5);
printf("exiting from main program\n");
pthread_join(th1, NULL);
pthread_join(th2, NULL);
return 0;
}

プログラムをコンパイルして実行すると、出力は次のようになります。

Hi from thread Y
Hi from thread X
Hi from thread Y
...
Hi from thread Y
exiting from main program
Hi from thread X
...
Hi from thread X
Thread X done!
Hi from thread Y
Thread Y done!

スレッド終了

対応するpthread_t idを渡すことで、pthread_cancel を呼び出してスレッドをキャンセルできます。

int pthread_cancel(pthread_t thread);

これは、次のコードで実際に確認できます。繰り返しますが、主な機能のみが異なります。

int main(void)
{
pthread_t th1, th2;
pthread_create(&th1, NULL, worker, "X");
pthread_create(&th2, NULL, worker, "Y");
sleep(1);
printf("====> Cancelling Thread Y!!\n");
pthread_cancel(th2);
usleep(100000);
printf("====> Cancelling Thread X!\n");
pthread_cancel(th1);
printf("exiting from main program\n");
return 0;
}

実行中のスレッドがキャンセルされていることを示すプログラムからの出力

スレッドが作成される理由

オペレーティング システムは常に、自己作成リストまたはユーザー作成スレッド リストのいずれかから、1 つ以上の CPU でスレッドを実行しようとします。一部のスレッドは、ハードウェアからの入出力信号を待機しているため、実行できません。また、自発的に待機している、別のスレッドからの応答を待っている、または別のスレッドにブロックされている可能性もあります。

pthread を使用して作成したスレッドに割り当てるリソースを調整できます。これは、カスタム スケジューリング ポリシーにすることも、必要に応じて FIFO やラウンドロビンなどのスケジューリング アルゴリズムを選択することもできます。

コメントを残す

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