Aritalab:Lecture/Programming/C/Parallel
From Metabolomics.JP
Posix スレッド
Pthreads とは pthread.h ヘッダーファイルで定義される C 言語用のライブラリです。これを用いてメモリ共有型の並列プログラムを組むことができます。UNIX の gcc でコンパイルする時は gcc -pthread オプションをつけて下さい (cygwin環境ではつけなくてもコンパイル可能)。これから以下の 3 項目について解説します。
- 排他制御 (mutual exclusion または mutex と呼ばれる処理。ロックの概念)
- 条件変数(ウェイトとシグナル)
- 同期
関連サイトとしてはローレンスリバモア国立研究所のチュートリアルが秀逸です。並列プログラミング全般やOpenMPの解説もあります。まずは Java によるプログラミングの項目をみて、排他制御と条件変数の使い方を理解しておいて下さい。
スレッドの作成、排他制御
具体例をみるのが一番なので、とにかく C コードを示すようにします。まず、i番目のスレッドがそれぞれ 100i から 100*(i+1) までの数を足し算してそれらの総和を計算するプログラムをみてみます。
#include<stdio.h>
#include<pthread.h>
#define NTHREAD 3
/* スレッドに渡すデータはグローバル変数にする */
int global_sum[NTHREAD+1];
/* スレッドの終了を待つための id 格納場所 */
pthread_t callThd[NTHREAD];
/* グローバル変数書き込み用ロック */
pthread_mutex_t lock;
int main(void);
void* thread(int id, void* arg);
void* thread(int id, void* arg) {
int i, j, sum;
sum=0;
for(i=100*id; i <= 100*(id+1); i++)
sum += i;
pthread_mutex_lock(&lock);
global_sum[id] = sum;
global_sum[NTHREAD] += sum;
/* グローバル変数の状態表示。ロック外に出しても良い */
printf("summing [");
for(j=0; j < NTHREAD; j++)
printf("%d ", global_sum[j]);
printf("] = ");
printf("%d\n", global_sum[NTHREAD]);
pthread_mutex_unlock(&lock);
pthread_exit((void*)0);
}
int main(void) {
long i;
void *status;
pthread_attr_t attr;
pthread_mutex_init(&lock, NULL);
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_t tid;
for(i=0; i < NTHREAD; i++)
pthread_create(&callThd[i], &attr, thread, (void*)i);
pthread_attr_destroy(&attr);
for(i=0; i < NTHREAD; i++)
pthread_join(callThd[i], &status);
pthread_mutex_destroy(&lock);
pthread_exit(NULL);
}
- ロック変数
- pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
のように定義します。最初に PTHREAD_MUTEX_INITIALIZER と値を入れても良いし、main 関数内から pthread_mutex_init(&lock, NULL); と呼んでも同じです。当たり前ですが、変数は最初 unlock されています。終了する時は pthread_mutex_destroy(&lock); を忘れずに呼んで下さい。
- スレッドの状態
- pthread_attr_setdetachstate 関数では、PTHREAD_CREATE_DETACHED または PTHREAD_CREATE_JOINABLE という属性を与えられます。前者はスレッドが終了するのを待たずに、main()スレッドが終了してよいことを示します。(上のプログラムでは join して結果を待っています。)その際、 main 関数で pthread_exit(NULL) するのを忘れないようにして下さい。そうしないと main 関数とともにプログラム自体も終了してしまいます。
- ロックの仕方
- 各スレッドは pthread_mutex_lock(&lock) でロックをかけますが、もし既にロックされている場合はアンロックされるまでトライし続けます。ここで取得エラーを返すには pthread_mutex_trylock(&lock) 関数を使います。