目次
1.1 スレッドの作成: pthread_create 関数
1.5 スレッドのキャンセル: pthread_cancel 関数
1.6 スレッドキャンセルステータス: pthread_setcancelstate 関数
1.7 スレッドキャンセルポイント: pthread_testcancel 関数
1.8 スレッドキャンセルタイプ:pthread_setcanceltype関数
1.9 スレッド出口クリーニング関数: pthread_cleanup_push 関数と pthread_cleanup_pop 関数
1.9.1 pthread_exit 関数を呼び出すと、システムは自動的にスレッド クリーンアップ関数を呼び出します。
1.9.2 スレッドがキャンセルされると、システムは自動的にスレッド クリーンアップ関数を呼び出します
1.9.3 pthread_cleanup_pop 関数を呼び出すと、システムは自動的にスレッド クリーニング関数を呼び出します
0. スレッドの概念
糸の概念
- 各プロセスには独自のデータ セグメント、コード セグメント、スタック セグメントがあり、プロセスの作成、切り替え、キャンセル時に大きなシステム オーバーヘッドが必要になります。
- オーバーヘッドを削減するために、スレッドはプロセスから進化しました。
- スレッドはプロセス内に存在し、プロセスのリソースを共有します。
- スレッドはプロセス内の独立した制御フローであり、環境 (レジスタ バンクやプログラム カウンタを含む) と一連の実行命令で構成されます。
- 各プロセスにはアドレス空間と制御スレッドがあります。
スレッドとプロセスの比較
スケジュール設定:
スレッドは、CPU のスケジューリングとディスパッチの基本単位です。
リソースを持っている:
プロセスは、システム内のプログラム実行とリソース割り当ての基本単位です。
スレッド自体は通常、リソースを所有しません (必須のプログラム カウンター、レジスタのセット、およびスタックを除く) が、プロセス コード セグメント、データ セグメント、システム リソースなど、スレッドが属するプロセスのリソースにアクセスできます。 (ファイル、I/O デバイスなどを開く)。
システムのオーバーヘッド:
同じプロセス内の複数のスレッドは同じアドレス空間を共有できるため、スレッド間の同期と通信が容易になります。
プロセスの切り替えでは、現在のプロセス全体の CPU 環境の保存と、新しくスケジュールされたプロセスの CPU 環境の設定が行われますが、スレッドの切り替えでは、少数のレジスタの内容の保存と設定のみが必要で、メモリ管理操作は必要ありません。したがって、システム リソースをより効率的に使用し、システムのスループットを向上させることができます。
同時実行性:
プロセスを同時に実行できるだけでなく、プロセス内の複数のスレッドを同時に実行することもできます。
要約:
スレッドは一般に軽量プロセスと呼ばれます。
プロセスは複数のスレッドを作成でき、複数のスレッドがプロセスのリソースを共有します。
各プロセスが作成されると、システムはそれに 4G 仮想メモリを与えます。3G ユーザー空間はプライベートであるため、プロセスが切り替わるとユーザー空間も切り替わり、システムのオーバーヘッドが増加します。プロセス内の複数のスレッドがメモリを共有します。プロセスのリソースを共有するため、スレッド切り替え時にこれらのリソースを切り替える必要がなく、効率が高くなります。
スレッドのスケジューリング メカニズムはプロセスのスケジューリング メカニズムと同じで、複数のスレッドが前後に切り替わります。
マルチスレッドの用途:
マルチタスクプログラムの設計:
プログラムはアプリケーションを処理し、複数のタスクを処理する必要がある場合があります。それらを処理するために別のプロセスを開発すると、システムのオーバーヘッドが大きくなり、データ共有が発生し、プログラム構造が不便になります。この場合、マルチスレッドプログラミング手法が使用できます。使用済み。
同時プログラミング:
タスクは完了するために複数の異なるステップに分割される場合があり、これらの異なるステップは疎結合であり、スレッドの相互排他によって同期的かつ同時に完了する場合があります。これにより、さまざまなタスク ステップのスレッドを作成できます。
ネットワークプログラミング:
ネットワーク利用の効率を向上させるために、マルチスレッドを使用し、各接続に対して 1 つのスレッドを使用して処理する場合があります。
データ共有:
- 同じプロセス内の異なるスレッドはプロセスのデータ空間を共有し、異なるスレッド間でのデータ共有を容易にします。
- マルチ CPU システムでは、真の並列処理が実現されます。
1. スレッドの基本動作
- すべてのプロセスにプロセス番号があるのと同じように、すべてのスレッドにもスレッド番号があります。
- プロセス番号はシステム全体で一意ですが、スレッド番号は異なり、スレッドはそれが属するプロセス環境内でのみ有効です。
- プロセス番号は pid_t データ型で表され、負ではない整数です。スレッド番号は pthread_t データ型で表されます。
- 一部のシステムでは、pthread_t を実装するときに構造体を使用してそれを表現するため、ポータブル オペレーティング システムの実装では整数として処理できません。
1.1 スレッドの作成: pthread_create 関数
pthread_create 関数:
#include<pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);
関数:
スレッドを作成します。
パラメータ:
thread: スレッド識別子のアドレス。
attr: スレッド属性構造体のアドレス。
start_routine: スレッド関数のエントリアドレス。
arg: スレッド関数に渡されるパラメータ。
戻り値:
成功: 0
失敗: 非 0
fork との違いは、pthread_create で作成されたスレッドは親スレッドと同じ時点で実行を開始するのではなく、指定された関数から実行を開始し、関数の実行終了後にスレッドが終了することです。
スレッドはプロセスに依存して存在するため、スレッドを作成したプロセスが終了すると、スレッドも終了します。
スレッド関数プログラムは pthread ライブラリ内にあるため、リンク時にパラメータ -lpthread を追加する必要があります。
コード例:
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
void* thread_fun(void* arg)
{
printf("son1 if running\n");
}
int main()
{
printf("man if running \n");
pthread_t thread;
if (pthread_create(&thread, NULL, thread_fun, NULL) != 0)
{
perror("fail to pthread_create");
exit(1);
}
while (1);
return 0;
}
実行のスクリーンショット:
スレッドスケジューリング機構の検証
コード例:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>
void* pthread_fun1(void* arg)
{
printf("son1 if running\n");
sleep(1);
printf("*****************************\n");
}
void* pthread_fun2(void* arg)
{
printf("son2 if running\n");
sleep(1);
printf("----------------------------\n");
}
int main()
{
pthread_t thread1, thread2;
if (pthread_create(&thread1, NULL, pthread_fun1, NULL) != 0)
{
perror("fail to pthread_create");
exit(1);
}
if (pthread_create(&thread2, NULL, pthread_fun2, NULL) != 0)
{
perror("fail to pthread_create");
exit(1);
}
while (1);
return 0;
}
実行のスクリーンショット:
スレッド処理関数のパラメータ受け渡し
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
int num = 100;
void* pthread_fun1(void* srg)
{
printf("son1:num = %d\n", num);
num++;
int n = *(int*)srg;
printf("son1 n = %d\n", n);
}
void* pthread_fun2(void* arg)
{
sleep(1);
printf("son2:num =%d\n", num);
int n = *(int*)arg;
printf("son2 n = %d\n", n);
}
int main()
{
pthread_t thread1, thread2;
int a = 555;
if (pthread_create(&thread1, NULL, pthread_fun1, (void*)&a) != 0)
{
perror("'fail too pthred_creat");
exit(1);
}
if (pthread_create(&thread2, NULL, pthread_fun2, (void*)&a) != 0)
{
perror("fail to pthread_crateee");
exit(1);
}
while (1);
return 0;
}
実行のスクリーンショット:
1.2 待機中のスレッド: pthread_join 関数
pthread_join関数
#include<pthread.h>
int pthread_join(pthread_t thread, void **retval);
関数:
サブスレッドが終了するのを待って、サブスレッドのリソースをリサイクルします。
パラメータ:
thread: 待機中のスレッド番号。
retval: スレッドの終了ステータスを格納するために使用されるポインターのアドレス。
戻り値:
成功: 0
失敗: 非 0
コード例:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
void* thread_fun(void* arg)
{
printf("son is running\n");
sleep(3);
printf("son will quit\n");
}
int main()
{
printf("man is running\n");
pthread_t thread;
if (pthread_create(&thread, NULL, thread_fun, NULL) != 0)
{
perror("fail to pthhread_create");
exit(1);
}
if (pthread_join(thread, NULL) != 0)
{
perror("fail toooo pthread_join");
exit(1);
}
printf("man will quit\n");
return 0;
}
実行のスクリーンショット:
子スレッドの終了ステータス値を取得します。
コード例:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
void* thread_fun(void* arg)
{
static int num = 666;
printf("son is running\n");
sleep(3);
printf("son will quit\n");
return (void*)#
}
int main()
{
printf("man is running\n");
pthread_t thread;
if (pthread_create(&thread, NULL, thread_fun, NULL) != 0)
{
perror("fail to pthhread_create");
exit(1);
}
int* num;
if (pthread_join(thread, (void**)&num) != 0)
{
perror("fail toooo pthread_join");
exit(1);
}
printf("ret_val = %d\n", *num);
printf("man will quit\n");
return 0;
}
実行のスクリーンショット:
1.3 スレッド分離: pthread_detach 関数
スレッドの結合状態と分離状態
Linux スレッドの実行は Windows とは異なり、pthread には 2 つの状態があります。
参加可能または分離可能であり、スレッドはデフォルトで参加可能な状態で作成されます。
スレッドが結合可能な状態にある場合、スレッドが占有しているスタックとスレッド記述子 (合計 8K 以上) は、スレッド関数が戻って終了するとき、または pthread_exit のときに解放されません。これらのリソースは、pthread_join を呼び出した後にのみ解放されます。
デタッチ状態のスレッドの場合、スレッド関数の終了時または pthread_exit 時にこれらのリソースが自動的に解放されますので、pthread_detach 関数を使用してスレッドをデタッチ状態に設定します。
スレッドを作成した後、そのリソースをリサイクルする必要がありますが、 pthread_join 関数を使用すると呼び出し元がブロックされてしまうため、Linux にはスレッド分離関数が用意されています。
pthread_detach 関数
#include<pthread.h>
int pthread_detach(pthread_t スレッド);
関数:
呼び出し元のスレッドを現在のプロセスから切り離し、独立したスレッドにします。スレッドが終了すると、システムは自動的にリソースを再利用します。
パラメータ:
スレッド: スレッド番号。
戻り値:
成功: 0
失敗: 非 0
コード例:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
void* pthread_fun(void* arg)
{
printf("son is running\n");
sleep(2);
printf("son will quite\n");
}
int main()
{
printf("man is running\n");
pthread_t thread;
if (pthread_create(&thread, NULL, pthread_fun, NULL) != 0)
{
perror("fail to pthread_create\n");
exit(1);
}
//这里就不需要再执行pthread_join了
if (pthread_detach(thread) != 0)
{
perror("fail to pthread_detach\n");
exit(1);
}
while (1)
{
printf("hello world\n");
sleep(1);
}
return 0;
}
実行のスクリーンショット:
1.4 スレッドの終了: pthread_exit 関数
プロセスでは exit 関数または _exit 関数を呼び出してプロセスを終了することができますが、スレッドでは次の 3 つの方法でプロセス全体を終了せずに制御フローを停止することができます。
- スレッドが関数の実行から戻ります
- スレッドは pthread_exit を呼び出してスレッドを終了します
- スレッドは同じプロセス内の他のスレッドによってキャンセルされる可能性があります
スレッド終了関数: pthread_exit 関数
#include<pthread.h>
void pthread_exit(void *retval);
関数:
呼び出し元のスレッドを終了します。
パラメータ:
retval: スレッドの終了ステータスを格納するポインタ。
注記:
プロセス内の複数のスレッドはプロセスのデータ セグメントを共有するため、通常、スレッドが占有しているリソースは終了後に解放されません。
リソースを解放したい場合は、結合状態が pthread_join 関数を通過する必要があり、分離状態は自動的に解放されます。コード例:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
void* pthread_fun(void* arg)
{
static char buf[] = "this thread has quied";
printf("son if running\n");
int i = 0;
for (i = 0; i < 10; i++)
{
if (i == 5)
{
// pthread_exit(NULL);
pthread_exit(buf);
}
printf("**********************\n");
sleep(1);
}
}
int main()
{
printf("man is running\n");
pthread_t thread;
if (pthread_create(&thread, NULL, pthread_fun, NULL) != 0)
{
perror("fail to pthread_create");
exit(1);
}
char* str;
pthread_join(thread, (void**)&str);
printf("str = %s\n", str);
printf("man will quit\n");
return 0;
}
実行のスクリーンショット:
1.5 スレッドのキャンセル: pthread_cancel 関数
スレッドのキャンセルとは、実行中のスレッドをキャンセルする操作を指します。
#include<pthread.h>
int pthread_cancel(pthread_t スレッド);
関数:
スレッドをキャンセルします。
パラメータ:
thread: ターゲットのスレッド ID。
戻り値:
成功: 0
失敗: エラー番号
pthread_cancel 関数の本質は、ターゲット スレッド thread にシグナルを送信して、ターゲット スレッドを終了させることです。
この関数はターゲット スレッドに終了信号を送信するだけで、ターゲット スレッドのキャンセルが完了するまで待機せずに戻ります。
ただし、送信が成功しても対象のスレッドが確実に終了するわけではなく、スレッドをキャンセルする場合は、スレッドのキャンセル属性によってキャンセルできるかどうか、またいつキャンセルできるかが決まります。
スレッドのキャンセルステータス:
つまり、スレッドをキャンセルできるかどうかです。
スレッドのキャンセルポイント:
そこでスレッドがキャンセルされます。
スレッドのキャンセルの種類:
スレッドをキャンセルできる場合、すぐにキャンセルされますか、それともキャンセルポイントに達したときにキャンセルされますか?
コード例:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>
void* pthread_fun(void* arg)
{
while (1)
{
printf("son is running\n");
sleep(1);
}
}
int main()
{
pthread_t thread;
if (pthread_create(&thread, NULL, pthread_fun, NULL) != 0)
{
perror("fail to pthread_create");
exit(1);
}
sleep(3);
//任意线程都可以尝试取消
pthread_cancel(thread);
pthread_join(thread, NULL);
return 0;
}
実行のスクリーンショット:
1.6 スレッドキャンセルステータス: pthread_setcancelstate 関数
Linux システムでは、デフォルトでスレッドをキャンセルできます。プログラミング時に、pthread_setcancelstate 関数を通じてスレッドをキャンセルできるかどうかを設定できます。
#include<pthread.h>
int pthread_setcancelstate(int state, int *oldstate);
関数:
スレッドをキャンセルできるかどうかを設定します。
パラメータ:
状態:新品の状態。
PTHREAD_CANCEL_DISABLE: キャンセルできません。
PTHREAD_CANCEL_ENABLE: キャンセルできます。
oldstate: 呼び出しスレッドの元のキャンセル可能な状態を保存するメモリ アドレス。
戻り値:
成功: 0
失敗: 非 0
コード例:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>
void* pthread_fun(void* arg)
{
// pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
while (1)
{
printf("son is running\n");
sleep(1);
}
}
int main()
{
pthread_t thread;
if (pthread_create(&thread, NULL, pthread_fun, NULL) != 0)
{
perror("fail to pthread_create");
exit(1);
}
sleep(3);
pthread_cancel(thread);
pthread_join(thread, NULL);
return 0;
}
実行のスクリーンショット:
1.7 スレッドキャンセルポイント: pthread_testcancel 関数
スレッドがキャンセルされた後、スレッドはすぐには終了されません。デフォルトでは、スレッドはキャンセルポイントに達するまで終了できません。プログラミング時に、pthread_testcancel 関数を通じてスレッドのキャンセル ポイントを設定できます。
void pthread_testcancel(void);
#include<pthread.h>
void pthread_testcancel(void);機能: スレッドのキャンセルポイントを設定します。
パラメータ: なし
戻り値: なし
別のスレッドがこの関数を呼び出したスレッドをキャンセルした場合、キャンセルされたスレッドはこの関数を実行すると終了します。
POSIX.1 では、スレッドが表 1 と表 2 のいずれかの関数を呼び出すと、キャンセル ポイントが表示されることが保証されています。
表1:
表 2:
コード例:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>
void* pthread_fun(void* arg)
{
while (1)
{
printf("son is running\n");
sleep(1);
pthread_testcancel();
}
}
int main()
{
pthread_t thread;
if (pthread_create(&thread, NULL, pthread_fun, NULL) != 0)
{
perror("fail to pthread_create");
exit(1);
}
sleep(3);
pthread_cancel(thread);
pthread_join(thread, NULL);
return 0;
}
実行のスクリーンショット:
1.8 スレッドキャンセルタイプ:pthread_setcanceltype関数
スレッドがキャンセルされた後、スレッドはすぐには終了されません。デフォルトでは、スレッドはキャンセルポイントに達するまで終了できません。プログラミング時に、pthread_setcanceltype 関数を使用してスレッドをすぐにキャンセルできるかどうかを設定できます。
#include<pthread.h>
int pthread_setcanceltype(int タイプ, int *oldtype);
関数:
タイプ: タイプ
PTHREAD_CANCEL_ASYNCHRONOUS: すぐにキャンセルします。
PTHREAD_CANCEL_DEFERRED: すぐにキャンセルしないでください。
oldtype: 呼び出しスレッドの元のキャンセル可能な型のメモリ アドレスを保存します。
戻り値:
成功: 0
失敗: 非 0
コード例:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>
void* pthread_fun(void* arg)
{
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
// pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
while (1)
{
printf("son is running\n");
sleep(1);
}
}
int main()
{
pthread_t thread;
if (pthread_create(&thread, NULL, pthread_fun, NULL) != 0)
{
perror("fail to pthread_create");
exit(1);
}
sleep(3);
pthread_cancel(thread);
pthread_join(thread, NULL);
return 0;
}
実行のスクリーンショット:
1.9 スレッド出口クリーニング関数: pthread_cleanup_push 関数と pthread_cleanup_pop 関数
プロセスの終了クリーンアップと同様に、スレッドは終了時に呼び出される関数を登録することもできます。このような関数はスレッド クリーンアップ ハンドラーと呼ばれます。
知らせ:
スレッドは複数のクリーンアップ ハンドラーを確立できます。
ハンドラーはスタック上にあるため、登録された順序とは逆の順序で実行されます。
#include<pthread.h>
void pthread_cleanup_push(void (* ルーチン)(void *), void*arg);
関数:
Clear 関数をスタックにプッシュします。つまり、クリーンアップ機能を登録します。
パラメータ:
ルーチン: スレッド クリーンアップ関数へのポインタ。
arg: スレッド クリーニング関数に渡されるパラメータ。
ポップアップクリーニング機能
#include<pthread.h>
void pthread_cleanup_pop(int 実行);
関数:
クリーニング関数はスタックからポップされます。つまり、クリーニング関数は削除されます。
パラメータ:
execute: スレッドクリーンアップ関数の実行フラグ。
0 以外の場合、クリーニング機能がポップアップ表示され、クリーニング機能が実行されます。
0 の場合、クリーニング機能がポップアップ表示され、クリーニング機能は実行されません。
クリーンアップ関数は、スレッドが次のアクションを実行するときに呼び出されます。
- pthread_exit を呼び出してプロセスを終了します。
- 他のスレッドからのキャンセル要求に応答します。
- ゼロ以外の実行で pthread_cleanup_pop を呼び出します
どちらの場合も、pthread_cleanup_pop は、最後の pthread_cleanup_push 呼び出しによってテストされたクリーンアップ処理関数を削除します。
1.9.1 pthread_exit 関数を呼び出すと、システムは自動的にスレッド クリーンアップ関数を呼び出します。
コード例:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>
void mycleanup(void* arg)
{
printf("cleann up ptr = %s\n", (char*)arg);
free((char*)arg);
}
void* pthread_fun(void* arg)
{
char* ptr = NULL;
printf("this is new thread\n");
ptr = (char*)malloc(100);
pthread_cleanup_push(mycleanup, (void*)(ptr));
memset(ptr, 0, 100);
strcpy(ptr, "memory from malloc");
sleep(3);
printf("before exit\n");
pthread_exit(NULL);
printf("before pop\n");
pthread_cleanup_pop(1);
}
int main()
{
pthread_t thread;
pthread_create(&thread, NULL, pthread_fun, NULL);
pthread_join(thread, NULL);
printf("process is dying\n");
return 0;
}
実行のスクリーンショット:
1.9.2 スレッドがキャンセルされると、システムは自動的にスレッド クリーンアップ関数を呼び出します
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>
void mycleanup(void* arg)
{
printf("cleann up ptr = %s\n", (char*)arg);
free((char*)arg);
}
void* pthread_fun(void* arg)
{
char* ptr = NULL;
printf("this is new thread\n");
ptr = (char*)malloc(100);
pthread_cleanup_push(mycleanup, (void*)(ptr));
memset(ptr, 0, 100);
strcpy(ptr, "memory from malloc");
sleep(10);
// pthread_exit(NULL);
printf("before pop\n");
pthread_cleanup_pop(1);
}
int main()
{
pthread_t thread;
pthread_create(&thread, NULL, pthread_fun, NULL);
sleep(5);
printf("before cancel\n");
pthread_cancel(thread);
pthread_join(thread, NULL);
printf("process is dying\n");
return 0;
}
実行のスクリーンショット:
1.9.3 pthread_cleanup_pop 関数を呼び出すと、システムは自動的にスレッド クリーニング関数を呼び出します
コード例:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>
void cleanup_func1(void* arg)
{
printf("in clanup func1\n");
printf("clean up ptr = %s\n", (char*)arg);
free((char*)arg);
}
void cleanup_func2(void* arg)
{
printf("in cleanup func2\n");
}
void* pthread_fun(void* arg)
{
char* ptr = NULL;
printf("this is new thread\n");
ptr = (char*)malloc(100);
pthread_cleanup_push(cleanup_func1, (void*)(ptr));
pthread_cleanup_push(cleanup_func2, (void*)(ptr));
memset(ptr, 0, 100);
strcpy(ptr, "memory from malloc");
sleep(3);
printf("before pop\n");
pthread_cleanup_pop(1);
printf("before pop\n");
pthread_cleanup_pop(1);
}
int main()
{
pthread_t thread;
pthread_create(&thread, NULL, pthread_fun, NULL);
pthread_join(thread, NULL);
printf("process is dying\n");
return 0;
}
実行のスクリーンショット:
要約:
このブログでは、スレッドプログラミングに関連する関数を詳しく紹介します。これらの機能を理解して習得することで、スレッドをより柔軟に作成、管理、同期して、同時プログラミングとマルチタスクを実現できるようになります。これらの関数には、スレッドの作成、起動、待機、終了などが含まれており、マルチスレッド アプリケーションを作成するための重要なツールです。
スレッド関数の使用をマスターすると、効率的で信頼性の高いマルチスレッド アプリケーションを構築し、コンピューティング リソースを最大限に活用してパフォーマンスを向上させることができます。これらの関数を柔軟に適用することで、スレッド間の調整と通信を実現し、競合状態やデッドロックなどの同時プログラミングでよくある問題を回避できます。