Linuxシステムプログラミング56スレッド-条件変数

条件変数がある理由:相互排他ロックの欠如を補うため

ミューテックスロックのデメリット:スレッドは、ロックとロックフリーブロッキング(pthread_mutex_lock)でのみ実行できます。スレッドが共有データの変更が発生したかどうかに基づいて他の操作を実行する場合。次に、ミューテックスロックを使用して実装されている場合、スレッドは次の操作を繰り返します。

申请持锁成功
	判断 (共享数据的某个变化是否发生)
		如果有发生,执行其他操作
	如果没发生,解锁,(加sleep()会影响效率)并重新申请持锁
申请持锁失败 阻塞(pthread_mutex_lock) 等待持锁

共有データの特定の変更が発生するまでに長い時間がかかる場合、スレッドは上記の操作を繰り返し実行し、CPU処理時間の浪費を引き起こします。CPU処理時間を節約するためにsleep()を擬似コードに追加できると言う人もいますが、その結果、プロセスが共有データの特定の変更を最速で検出できることを保証できず、効率に影響します。つまり、同様の状況では、相互排他ロックには時間と効率の問題があります。

したがって、上記の時間と効率の問題を解決するために、条件変数が導入されます

条件変数
条件変数を使用すると、スレッドはブロックして、他のスレッドがシグナルを送信してウェイクアップするのを待つことができます。CPUの処理時間と効率を考慮すると、これは相互排他ロックの欠如を補います。


相関連関数数:
pthread_cond_destroy()
pthread_cond_init()
pthread_cond_broadcast()
pthread_cond_signal()
pthread_cond_timedwait()
pthread_cond_wait()

pthread_cond_t 类型,条件变量类型


NAME
       pthread_cond_destroy, pthread_cond_init — destroy and initialize condition variables

SYNOPSIS
       #include <pthread.h>

//销毁
       int pthread_cond_destroy(pthread_cond_t *cond);

//动态初始化条件变量
       int pthread_cond_init(pthread_cond_t *restrict cond,
           const pthread_condattr_t *restrict attr);

//静态初始化条件变量
       pthread_cond_t cond = PTHREAD_COND_INITIALIZER;



NAME
       pthread_cond_broadcast, pthread_cond_signal — broadcast or signal a condition 广播或发出条件的信号

SYNOPSIS
       #include <pthread.h>

       //广播形式的唤醒,唤醒所有因 该条件变量cond而阻塞的线程
       int pthread_cond_broadcast(pthread_cond_t *cond);

       //唤醒 因为该条件变量cond而阻塞的所有线程中的任意一个线程
       int pthread_cond_signal(pthread_cond_t *cond);




NAME
       pthread_cond_timedwait, pthread_cond_wait — wait on a condition

SYNOPSIS
       #include <pthread.h>

//非阻塞等待条件变量变为真,超时等待
       int pthread_cond_timedwait(pthread_cond_t *restrict cond,
           pthread_mutex_t *restrict mutex,
           const struct timespec *restrict abstime);

//阻塞等待条件变量变为真,参数:条件变量,互斥量
       int pthread_cond_wait(pthread_cond_t *restrict cond,
           pthread_mutex_t *restrict mutex);

pthread_cond_wait()は、スレッドをブロックし、cond条件変数のブロックスレッドキューにスレッドをマウントしてからロックを解除し、最後にウェイクアップするのを待つために使用されます。目覚めた後、ロックが取得されます。ブロッキングスレッドが1つしかない場合は、目覚めた直後にロックが保持されます。

pthread_cond_waitを使用する方法は次のとおりです。

1    pthread _mutex_lock(&mutex)

2    while或if(线程执行的条件是否成立)
          pthread_cond_wait(&cond, &mutex);

3    线程执行

4    pthread_mutex_unlock(&mutex);

質問1:なぜ最初にロックを保持する必要があるのですか

回答:条件変数は、ミューテックスロックの不足を補うためのものです。どちらも、同時に1つのスレッドのみが重要なリソースにアクセスできるようにするため、ロックを追加する必要があります。

質問2:pthread_cond_wait()は、ロック保持ブロッキングをどのように防止しますか。つまり、ロックを解除せずにブロッキング状態に入ると、他のスレッドはこの行の重要なリソースにアクセスできなくなります。

回答:pthread_cond_wait()の内部実装は、最初にスレッドをブロッキングキューにマウントし、次にロックを解除することとして理解できます。そのため、関数内ではロックが解除されています。関数の内部ロック解除のタイミングは非常に重要です。関数は、スレッドをブロッキングキューにマウントした後にロック解除されます。これの目的は、スレッドをブロッキングキューにマウントする前に、他のスレッドが重要なリソースを変更できないようにすることです。最初にロックを解除してからマウントするとどうなるか想像してみてください。ロックを解除した後、他のスレッドがロックを取得し、重要なリソースを変更し、シグナルpthread_cond_signal()を送信すると、このスレッドはこの重要なリソースの変更に応答できなくなります。

シナリオ:
スレッドはロックを保持し、条件が確立されているかどうかを判断します。確立されていない場合、pthread_cond_wait()はスレッドをブロックし、cond条件変数のブロックスレッドキューにスレッドをマウントしてからロックを解除し、最後に待機します。目覚め。pthread_cond_wait()は、ウェイクアップ後にロックを取得します。ブロッキングスレッドが1つしかない場合は、ウェイクアップ後に直接ロックを保持し、条件が真であるかどうかの判断を開始します。確立されている場合は、ループから飛び出して次のステップに進みます。

実験1:
条件変数通知方式で実装:4つのスレッドが指定された時間内にa、b、c、dを順番に端末に連続的に出力し、各スレッドが文字を出力する

規範的記述:条件変数を使用して通知方法を実現する

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

#define THRNUM 4

static int num = 0;

static pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;

static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

static int next(int n)
{
	if(n+1 == THRNUM)
		return 0;
	return n+1;

}

static void *thr_add(void *p)
{
	int n = (int)p;//0 1 2 3
	int c = 'a' + (int)p;
	while(1)
	{
		pthread_mutex_lock(&mut);
		while(num != n)//判断是不是目标下一个线程
			pthread_cond_wait(&cond,&mut);//被唤醒的所有线程抢锁,抢到锁的线程 判断条件是否成立。。。
		write(1,&c,1);
		num = next(num);//准备唤醒下一个线程
		pthread_cond_broadcast(&cond);//惊群
		pthread_mutex_unlock(&mut);
	}
		

	pthread_exit(NULL);
} 

int main()
{
	pthread_t tid[THRNUM];
	int i,err;

	for(i = 0; i < THRNUM; i++)
	{
		err = pthread_create(tid+i,NULL,thr_add,(void *)i);
		if(err)
		{
			fprintf(stderr,"pthread_create(): %s\n",strerror(err));
			exit(1);
		}
	}

	//alarm(5);

	for(i = 0;i < THRNUM; i++)
	{
		pthread_join(tid[i],NULL);
	}

	pthread_mutex_destroy(&mut);
	pthread_cond_destroy(&cond);

	exit(0);
	
}

おすすめ

転載: blog.csdn.net/LinuxArmbiggod/article/details/114288246