スレッドの同期には,pthread_joinによる合流の他に,ロックという方法がある.
mutexって何だ?
mutexの属性と初期化
mutexのロックとアンロック
サンプル
mutexって何だ?
mutexはmutual exclusionの略らしい. ということは,mutexは「ミューテクス」とかそんな感じの読みのような気がするが,正しい読みはよくわからない(※). とにかく,pthreadでは,mutexをロックすることによって,スレッド間の同期を行うことができる.
mutex = バイナリセマフォ
ということで,よいと思う. つまり,あるスレッドでロックしたmutexに対し,別のスレッドがロックしようとしたら,最初のスレッドがそのmutexを開放するまで後のスレッドは待たされる.

上図で,破線で囲まれた部分の処理が,同期が保証される領域で,クリティカルセクションという. 具体的にはクリティカルセクションでは,複数のスレッドで参照,更新するデータに対する処理を行う. クリティカルセクションで参照するデータは,別のスレッドによって更新途中になっている不完全なデータを参照したり更新したりしてしまう心配がない. 性能の観点からすると,クリティカルセクションは短い程よい.
※ 私はどういうわけか,「ムテフ」とか「マテフ」と読んでしまうが,これは,まぁ,間違ってるんだろうな.
mutexの属性と初期化
mutexはまず初期化する必要がある.
int pthread_mutex_init(pthread_mutex_t *mutex,
const pthread_mutex-attr_t *mutexattr);
pthread_mutex_initはmutexattarで指定した属性をもつmutexを作成する. Linuxではmutex属性はmutex kindのみであり,これには以下があり,ロック使用としたmutexがすでにロックされていた場合の挙動が異なる.
fast
対象のmutexが開放されるまで待つ.
recursive
すぐに返る. mutexにはロックされた回数が保持され,同数アンロックされるまでロック解除状態にはならない… 共有ロックで使用できそうな気もするが,ロック状態とロック解除状態は外からどうやって見分けるんだ?
error checking
ロック関数はすぐに返る. 返り値はEDEADLK.
mutexを破壊する関数もある.
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
仕様としては,破壊したmutexは使用してはならない. Linuxではこの関数は何もしないらしいが,OSやハードウェアによってはmutexのために何か資源を保持するかもしれないので,いらなくなったら破壊しておくのが行儀のよいコードらしい. 可搬性はないようだが,以下のマクロによって,簡単にmutexを初期化できる.
pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t recmutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
pthread_mutex_t errchkmutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
それぞれmutex kindがfast,recursive,error checkingであるmutexを作成する.
mutexのロックとアンロック
mutexを作成したら,あとは各スレッドにてロックしたりアンロックしたりする. ロックには
int pthread_mutex_lock(pthread_mutex_t *mutex));
を使用する. 引数に指定したmutexに対し,ロックをかける. 指定したmutexがロックされていた場合の挙動はmutex属性による.
int pthread_mutex_trylock(pthread_mutex_t *mutex);
は,基本的にpthread_mutex_lockと同様だが,mutex種別がfastで,指定したmutexがロック中でもすぐに返る. この際の返り値はEBUSY.
int pthread_mutex_unlock(pthread_mutex_t *mutex);
では,指定したmutexをアンロックする. mutex属性がerror checkingであれば,指定したmutexがロックされていなかったり,呼び出したスレッドがロックしたスレッドでなければエラーになる. 逆に言うと,mutex属性がfastやrecursiveだとロックされたmutexを別のスレッドでアンロックできるようになっているが,可搬性のためには使用すべきでない.
サンプル
ではサンプルを示す.
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#define MY_ABORT(__COMMENT__) \
fprintf (stderr, "\n" __FILE__ "(%d) " __COMMENT__ "\n", __LINE__); \
exit (1);
#define MY_THREAD_ABORT(__COMMENT__) \
fprintf (stderr, "\n" __FILE__ "(%d) " __COMMENT__ "\n", __LINE__); \
pthread_exit(NULL);
/*
* スレッドパラメータ格納用
*/
typedef struct {
int var1;
int var2;
int sum;
int printFlag;
pthread_mutex_t printLock;
} SHARED_THREAD_ARG;
/*
* 表示スレッドイニシャル関数
*/
void *printThread(void *arg)
{
SHARED_THREAD_ARG *my_thread_arg =(SHARED_THREAD_ARG*)arg;
struct timespec sleepTime;
sleepTime.tv_sec = 0;
sleepTime.tv_nsec = 0.1 * 1000 * 1000;
while(my_thread_arg->printFlag > 0)
{
#ifdef SYNC
if (pthread_mutex_lock(&my_thread_arg->printLock) != 0)
{
MY_THREAD_ABORT("faield to mutex lock");
}
#endif
printf("%d + %d = %d\n",
my_thread_arg->var1,
my_thread_arg->var2,
my_thread_arg->sum);
#ifdef SYNC
if (pthread_mutex_unlock(&my_thread_arg->printLock) != 0)
{
MY_THREAD_ABORT("failed to mutex unlock");
}
#else
#endif
nanosleep(&sleepTime, NULL);
}
return 0;
}
/*
* 計算スレッドイニシャル関数
*/
void *calcurateThread(void *arg)
{
SHARED_THREAD_ARG *my_thread_arg =(SHARED_THREAD_ARG*)arg;
struct timespec sleepTime;
sleepTime.tv_sec = 0;
sleepTime.tv_nsec = 0.5 * 1000 * 1000;
int i = 0;
for (i = 0; i < 3; i++)
{
#ifdef SYNC
if (pthread_mutex_lock(&my_thread_arg->printLock) != 0)
{
MY_THREAD_ABORT("failed to mutex lock");
}
#endif
nanosleep(&sleepTime, NULL);
my_thread_arg->var1++;
nanosleep(&sleepTime, NULL);
my_thread_arg->var2++;
nanosleep(&sleepTime, NULL);
my_thread_arg->sum = my_thread_arg->var1
+ my_thread_arg->var2;
#ifdef SYNC
if(pthread_mutex_unlock(&my_thread_arg->printLock) != 0)
{
MY_THREAD_ABORT("failed to mutex lock");
}
#endif
}
my_thread_arg->printFlag = 0;
return 0;
}
int main(int argc,char *argv[])
{
int status;
void *thread_return;
// スレッドa,b共有パラメータ
SHARED_THREAD_ARG sharedArg = {0, 0, 0, 1, PTHREAD_MUTEX_INITIALIZER};
pthread_t thread_a;
// スレッドaを生成
status=pthread_create(&thread_a, NULL, printThread, &sharedArg);
if(status!=0){
MY_ABORT("failed to create thread_a");
}
pthread_t thread_b;
// スレッドbを生成
status=pthread_create(&thread_b, NULL, calcurateThread, &sharedArg);
if(status!=0){
MY_ABORT("failed to create thread_b");
}
// スレッドaが終了するのを待つ.
status = pthread_join(thread_a, &thread_return);
if (status != 0) {
MY_ABORT("failed to join thread_a");
}
// スレッドbが終了するのを待つ.
status = pthread_join(thread_b, &thread_return);
if (status != 0) {
MY_ABORT("failed to join thread_b");
}
fprintf(stderr, "\n");
return 0;
}