[Linuxアプリケーションプログラミング] POSIXスレッドの相互排除と同期メカニズムの障壁



1はじめに

  前の記事で説明した相互排除メカニズムでは、ミューテックス、読み取り/書き込みロック、スピンロックはすべて、単一のスレッドの相互排除に使用されます。スレッドがリソース(ロック)を申請すると、スレッドは実行を開始します。実際のアプリケーションでは、「リソースを待機している複数のスレッド」の同期シナリオがあります。つまり、リソースを取得した後、すべてのスレッドを同時に実行する準備ができています。バリアメカニズムは、複数のスレッドが並列(同期)で実行されるシナリオで使用されます。


2バリア

  バリアは、複数のスレッドを調整して同期的に実行し、スレッドを待機状態にするための同期メカニズムです。A、B、Cの3つのスレッドがバリアメカニズムを使用していると仮定すると、Aが最初に準備ができている可能性があります。BとCが準備ができていないため、Aはこの時点で待機状態です。準備ができているか、ユーザーが指定した状態に達しているのは3つのスレッドだけです。スレッドは同時に実行されます。この点は、条件変数の「ブロードキャスト信号」に似ています。複数のスレッドが条件変数信号を待機します。特定のスレッドが条件変数信号をブロードキャストすると、待機中のすべてのスレッドが実行のために起こされます。


2.1バリア特性

  • スレッド同期
  • 複数のスレッドを調整して同期的に実行する

2.2バリアアプリケーションのシナリオ

   バリアは、相互排他ロック、読み取り/書き込みロック、スピンロックとは異なります。バリアは、共有リソースの保護には使用されませんが、条件変数の機能に似ています。これらは、スレッドの同期に使用され、複数のスレッドの同期に使用されます。

  • 複数のスレッドの同期実行

2.3バリア使用の原則

  • なし

3バリア使用

  バリアを使用するための基本的な手順は次のとおりです。

[1]バリアインスタンスを作成する

[2]初期化の障壁

[3]バリアリソースを待機しています

[5]破壊障壁


3.1バリアを作成する

  posixスレッドバリアpthread_barrier_tデータ構造によって表さます。Mutexインスタンスは静的および動的に作成できます。

pthread_barrier_t barrier;

3.2バリアを初期化する

  バリア初期化は、pthread_barrier_init関数を使用した動的初期化のみをサポートします。

int pthread_barrier_init(pthread_barrier_t *barrier,
                        const pthread_barrierattr_t *attr,
                        unsigned count);
  • barrier、バリアインスタンスアドレスはNULLにできません
  • attr、バリア属性アドレス、NULLはデフォルト属性を使用することを意味します。デフォルト以外の属性は、プロセススコープとして指定する必要がありますPTHREAD_PROCESS_PRIVATE
  • count、待機中のスレッドの数
  • 戻り、成功した場合は0を返し、パラメーターが無効な場合はEINVALを返します。

3.3バリアリソースの待機

int pthread_barrier_wait(pthread_barrier_t *barrier);
  • barrier、バリアインスタンスアドレスはNULLにできません

  • 戻り値、2つの成功した戻り値があります。パラメータが無効で、EINVALを返します

    [1]最後に待機しているスレッド呼び出しが返されますPTHREAD_BARRIER_SERIAL_THREAD(-1)。このフラグを使用して、最後に実行されたスレッドを区別するなどの特別なタスクを処理できます。

    [2]他のスレッドからの呼び出しは0を返します

  pthread_barrier_waitこの関数は、スレッドの準備ができるまで待機し、スレッド処理タスクでそれを呼び出し、すべてのスレッドの準備ができた後で実行を開始するために使用されます。

void thread_entry(void*)
{
    
    
    pthread_barrier_wait(&barrier);/* 等待就绪 */
    /* todo 线程处理任务*/
}

3.4バリア破壊

int pthread_barrier_destroy(pthread_barrier_t *barrier);
  • barrier、バリアインスタンスアドレスはNULLにできません
  • 戻り、成功した場合は0を返し、パラメーターが無効な場合はEINVALを返します。

  pthread_barrier_destroy動的初期化を使用したバリアを破壊するために使用されます。破壊されたバリアは初期化されていない状態にあり、バリアプロパティと制御ブロックパラメータは使用できない状態にあります。destroy関数を使用するときに注意すべき点がいくつかあります。

  • 破壊されたバリアはpthread_barrier_init再初期化できます
  • 破壊されたバリアは繰り返し破壊することはできません
  • バリアを保持しているスレッドがなく、バリアがスレッドをブロックしていない場合は、破棄できます。

3. 5例を書く

  コード実現関数:

  • 3つのスレッドを作成する
  • 3つのスレッドが個別に自己カウントを実行し、端末に出力します
  • 期待される結果、3つのスレッドが同期的に実行される
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <stdlib.h>
#include "pthread.h" 

#define USE_BARRIER	1	/* 是否使用屏障,1使用,0不使用 */

/* 屏障 */
#if USE_BARRIER
static pthread_barrier_t barrier;
#endif	

void *thread0_entry(void *data)  
{
    
    
	int temp =0;
	uint32_t count = 0;

	printf("thread0 startup\r\n");
#if USE_BARRIER
	temp = pthread_barrier_wait(&barrier);
	printf("thread0 barrier return[%d]\r\n", temp);
#endif
	for (;;)
	{
    
    
		printf("thread0 count [%d]\r\n", count++);
		sleep(1);	/* 主动让出cpu */
	}
}

void *thread1_entry(void *data)  
{
    
    
	int temp =0;
	uint32_t count = 0;

	sleep(1);	/* 让线程0先执行 */
	printf("thread1 startup\r\n");
#if USE_BARRIER	
	temp = pthread_barrier_wait(&barrier);
	printf("thread1 barrier return[%d]\r\n", temp);
#endif
	for (;;)
	{
    
    
		printf("thread1 count [%d]\r\n", count++);
		sleep(1);	/* 主动让出cpu */
	}
}

void *thread2_entry(void *data)  
{
    
    
	int temp =0;
	uint32_t count = 0;

	sleep(2);	/* 让线程1先执行 */
	printf("thread2 startup\r\n");
#if USE_BARRIER
	temp = pthread_barrier_wait(&barrier);
	printf("thread2 barrier return[%d]\r\n", temp);
#endif
	for (;;)
	{
    
    
		printf("thread2 count [%d]\r\n", count++);
		sleep(1);	/* 主动让出cpu */
	}
}

int main(int argc, char **argv)  
{
    
    
	pthread_t thread0,thread1,thread2;  
	void *retval;
	
#if USE_BARRIER
	pthread_barrier_init(&barrier,NULL,3);
#endif
    pthread_create(&thread0, NULL, thread0_entry, NULL);
	pthread_create(&thread1, NULL, thread1_entry, NULL);
	pthread_create(&thread2, NULL, thread2_entry, NULL);
    pthread_join(thread0, &retval);
    pthread_join(thread1, &retval);
	pthread_join(thread2, &retval);
	
	return 0;
 }

禁止されていない結果

  バリアがない場合、スレッド0が実行され、次にスレッド1が実行され、最後にスレッド2が実行されます。

acuity@ubuntu:/mnt/hgfs/LSW/STHB/pthreads/Barrier$ gcc barrier.c -o barrier -lpthread
acuity@ubuntu:/mnt/hgfs/LSW/STHB/pthreads/Barrier$ ./barrier
thread0 startup
thread0 count [0]
thread1 startup
thread1 count [0]
thread0 count [1]
thread2 startup
thread2 count [0]

バリアを使用した結果

  バリア待機を増やした後、スレッド0の準備ができていても、スレッド1とスレッド2 sleepはアクティブな中断のため準備ができていない状態です。スレッド0はブロックし、スレッド1とスレッド2の準備ができるまで待機してから、3つのスレッドが実行されます。

acuity@ubuntu:/mnt/hgfs/LSW/STHB/pthreads/Barrier$ gcc barrier.c -o barrier -lpthread
acuity@ubuntu:/mnt/hgfs/LSW/STHB/pthreads/Barrier$ ./barrier
thread0 startup
thread1 startup
thread2 startup
thread2 barrier return[-1]
thread2 count [0]
thread1 barrier return[0]
thread1 count [0]
thread0 barrier return[0]
thread0 count [0]
thread1 count [1]
thread2 count [1]
thread0 count [1]

  コードではsleep、スレッドの時間のかかるプロセスシミュレートするために関数が使用されます。実際のシナリオはこのように使用されない場合があります。これは単なるシミュレーションシナリオテストです。


4バリア特性

  デフォルトのバリアロック属性を使用すると、ほとんどのアプリケーションシナリオを満たすことができます。現在、POSIXバリアメカニズム属性は「スコープ」のみをサポートし、当面は「インプロセススコープ」のみをサポートしPTHREAD_PROCESS_PRIVATEます。最初にバリアプロパティの設定に慣れることができます。基本的な手順は次のとおりです。

[1]バリアプロパティのインスタンスを作成する

【2】初期プロパティインスタンス

【3】属性設定

[4]プロパティインスタンスの破棄


4.1バリアプロパティを作成する

  posixスレッドバリア属性pthread_barrierattr_tデータ構造で表さます。バリアプロパティのインスタンスは、静的および動的に作成できます。

pthread_barrierattr_t attr;

4.2バリア特性の初期化と破壊

int pthread_barrierattr_init(pthread_barrierattr_t *attr);
int pthread_barrierattr_destroy(pthread_barrierattr_t *attr);
  • attr、バリア属性インスタンスアドレスはNULLにできません
  • 成功した場合は0を返し、パラメータが無効な場合はEINVALを返します

  バリアプロパティを設定するときは、最初にプロパティpthread_barrierattr_tインスタンスを作成し、次にpthread_barrierattr_init関数の初期インスタンスを呼び出してから、プロパティ設定を呼び出します。初期化後の属性値は、デフォルトのバリアー属性です。これはpthread_barrier_init、デフォルトの属性でattr初期化を使用することと同じです(NULLを渡す)。


4.3バリアスコープ

  バリアスコープは、バリアのスコープを表し、インプロセス(作成者)スコープPTHREAD_PROCESS_PRIVATEとプロセス間スコープに分かれていますPTHREAD_PROCESS_SHAREDインプロセススコープはプロセス内のスレッド同期にのみ使用でき、プロセス間はシステムのすべてのスレッド間の同期に使用できます。

  現在、バリアはインプロセススコープのみをサポートしていますPTHREAD_PROCESS_PRIVATE

スコープ設定と取得機能:

int pthread_barrierattr_setpshared(pthread_barrierattr_t *attr, int pshared);
int pthread_barrierattr_getpshared(const pthread_barrierattr_t *attr, int *pshared);
  • attrバリア属性インスタンスのアドレス。NULLにはできません
  • pshared、スコープタイプ、PTHREAD_PROCESS_PRIVATE
  • 成功した場合は0を返し、パラメータが無効な場合はEINVALを返します

5まとめ

  バリアは、複数のスレッドを調整して同期実行するための同期メカニズムです。準備ができていないスレッドがあると、バリアメカニズムのすべてのスレッドの準備ができるまで他のスレッドがブロックされ、すべてのスレッドが同期して実行されます。

おすすめ

転載: blog.csdn.net/qq_20553613/article/details/106357029