スレッド間の同期と相互排除
スレッドはプロセスリソースとアドレス空間を共有するため、これらのリソースを操作するときは、スレッド間のリソースアクセスの同期と相互排除を考慮する必要があります。以下では、主にPOSIXの2つのスレッド同期メカニズム、つまりmutexとセマフォを紹介します。これら2つの同期メカニズムは、相互に呼び出すことで実装できます。単一のミューテックスは、同時に利用可能なリソースが1つだけの場合に適しています。セマフォは、同時に利用可能な複数のリソースがある場合に適しています。
1. mutexスレッド制御
1.1相互排他ロックのプログラミング命令
ミューテックスは、共有リソースのアトミック操作を制御するための簡単なロック方法です。mutexにはロックとロック解除の2つの状態しかないため、ある意味ではmutexをグローバル変数と見なすことができます。同時に、特定のミューテックスをマスターできるのは1つのスレッドだけであり、ロックされた状態のスレッドは共有リソースプロセスを操作できます。他のスレッドがすでにロックされているミューテックスをロックしたい場合、ロックされたスレッドがミューテックスを解放するまで、スレッドは中断されます。
mutexロックメカニズムには、次の基本機能を含めないでください。
- ミューテックスの初期化:pthread_mutex_init()
- ミューテックスロック:pthread_mutex_lock()
- ミューテックスロックの判定:pthread_mutex_trylock()
- ミューテックスのロック解除:pthread_mutex_unlock()
- ミューテックスロックを削除します。pthread_mutex_destory()
ミューテックスは、高速ミューテックス、再帰的ミューテックス、エラー検出ミューテックスに分類できます。一般的なデフォルト属性は高速ミューテックスです
- 高速ミューテックス:ミューテックスを所有するスレッドがロック解除されるまで、呼び出しスレッドはブロックします
- 再帰的ミューテックス:正常に戻ることができ、呼び出しスレッドがミューテックスをロックする回数を増やす
- エラー検出ミューテックス:高速ミューテックスの非ブロッキングバージョン。即座に戻り、エラーメッセージを返します。
1.2 mutex関数の説明
pthread_mutex_init()
/*****pthread_mutex_init()*****/
函数原型:int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
传 入 值:mutex 互斥锁
mutexattr -->PTHREAD_MUTEX_INITIALIZER 创建快速互斥锁
-->PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP 创建递归互斥锁
-->PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP 创建检错互斥锁
返 回 值:成功返回0;失败返回错误码
pthread_mutex_lock()
pthread_mutex_trylock()
pthread_mutex_unlock()
pthread_mutex_destory()
/*****pthread_mutex_****()*****/
函数原型:int pthread_mutex_lock(pthread_mutex_t *mutex)
int pthread_mutex_trylock(pthread_mutex_t *mutex)
int pthread_mutex_unlock(pthread_mutex_t *mutex)
int pthread_mutex_destory(pthread_mutex_t *mutex)
传 入 值:mutex 互斥锁
返 回 值:成功返回0;失败返回-1
2.セマフォスレッド制御
2.1セマフォプログラミング命令
セマフォは、オペレーティングシステムで使用されるPVアトミック操作であり、プロセスまたはスレッド間の同期および相互排除で広く使用されています。セマフォは基本的に非負の正のカウンタであり、パブリックリソースへのアクセスを制御するために使用されます。
PVアトミック操作の原理は次のとおりです。PVアトミック操作は、整数カウンターセマフォsemに対する操作です。P演算はsemを1だけ減らし、V演算はsemを1だけ増やします。プロセス(またはスレッド)は、セマフォの値に基づいて、パブリックリソースにアクセスできるかどうかを決定します。セマフォsem> = 0の値の場合、プロセス(またはスレッド)はパブリックリソースにアクセスできます。逆に、セマフォsem <0の値の場合、プロセス(またはスレッド)はセマフォsemまでブロックします。値> = 0。
PVアトミック操作は、主に、プロセスまたはスレッド間の同期および相互排除の2つの典型的なケースで使用されます。相互排除に使用する場合、複数のプロセス(またはスレッド)が1つのセマフォsemのみを設定することがよくあります。同期に使用する場合、複数のセマフォが設定されることが多く、それらの間で順次実行されるように異なる初期値が配置されます。
2.2セマフォ関数の説明
スレッド間の同期と相互排除のための一般的な関数:
- セマフォを作成して初期化します:sem_init()
- P操作、セマフォ> 0の場合、セマフォの値は1だけ減少します。sem_wait()およびsem_trywait()、セマフォ<0の場合、前者はプロセスをブロックし、後者はすぐに戻ります
- V操作、セマフォの値に1を追加し、待機中のプロセスをウェイクアップするシグナルを送信:sem_post()
- セマフォの値を取得します:sem_getvalule()
- セマフォを削除:sem_destory()
/*****sem_init()*****/
函数原型:int sem_init(sem_t *sem, int pshared, unsigned int value)
传 入 值:sem 信号量指针
pshared 决定信号量能否在几个进程间共享,一般取0表示是当前进程的局部信号量
value 信号量初始化值
返 回 值:成功返回0;失败返回-1
sem_wait()
sem_trywait()
sem_post()
sem_getvalule()
sem_destory()
/*****sem_****()*****/
函数原型:int sem_wait(sem_t *sem)
int sem_trywait(sem_t *sem)
int sem_post(sem_t *sem)
int sem_getvalule(sem_t *sem)
int sem_destory(sem_t *sem)
传 入 值:sem 信号量指针
返 回 值:成功返回0;失败返回-1
3.関数の例
thread.cのコードに基づいて、ミューテックスロック機能が追加され、元々独立して無秩序であった複数のスレッドの順次実行を実現します
/*****thread_mutex.c*****/
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define THREAD_NUMBER 3 //线程数
#define REPEAT_NUMBER 5 //每个线程中的小任务数
#define DELAY_TIME_LEVELS 10.0 //小任务之间的最大时间间隔
pthread_mutex_t mutex;
void *thrd_func(void *arg){
int thrd_num = (int)arg;
int delay_time = 0,count = 0;
int res;
res = pthread_mutex_lock(&mutex); //互斥锁上锁
if(res){
printf("Thread %d lock failed\n",thrd_num);
pthread_exit(NULL);
}
printf("Thread %d is starting\n",thrd_num);
for(count = 0;count < REPEAT_NUMBER;count++){
delay_time = (int)(rand()*DELAY_TIME_LEVELS/(RAND_MAX)) + 1;
sleep(delay_time);
printf("\tThread %d: job %d delay = %d\n",thrd_num,count,delay_time);
}
printf("Thread %d finished\n",thrd_num);
pthread_exit(NULL);
}
int main(){
pthread_t thread[THREAD_NUMBER];
int no = 0,res;
void * thrd_ret;
srand(time(NULL));
pthread_mutex_init(&mutex,NULL); //互斥锁初始化
for(no = 0;no < THREAD_NUMBER;no++){
res = pthread_create(&thread[no],NULL,thrd_func,(void*)no);
if(res != 0){
printf("Create thread %d failed\n",no);
exit(res);
}
}
printf("Create thread sucess\nWaiting for threads to finish...");
for(no = 0;no < THREAD_NUMBER;no++){
res = pthread_join(thread[no],&thrd_ret);
if(!res)
printf("Thread %d joined\n",no);
else
printf("Thread %d join failed\n",no);
pthread_mutex_unlock(&mutex); //互斥锁解锁
}
pthread_mutex_destory(&mutex);
return 0;
}
実行結果は次のとおりです。3つのスレッド間の実行順序は、スレッドが作成された順序と同じであることがわかります。
linux@linux-virtual-machine:~/andy/proc$ ./thread_mutex
Create threads sucess
Waiting for threads to finish...
Thread 0 is starting
Thread 0: job 0 delay = 7
Thread 0: job 1 delay = 7
Thread 0: job 2 delay = 6
Thread 0 finished
Thread 0 joined
Thread 1 is starting
Thread 1: job 0 delay = 3
Thread 1: job 1 delay = 5
Thread 1: job 2 delay = 10
Thread 1 finished
Thread 1 joined
Thread 2 is starting
Thread 2: job 0 delay = 6
Thread 2: job 1 delay = 10
Thread 2: job 2 delay = 8
Thread 2 finished
Thread 2 joined