マルチスレッドプログラミングでスレッド間で共有データにアクセスするときに、 mutexを用いて、排他ロックを行うことがあります。 スレッドの同期には,pthread_joinによる合流の他に、ロックという方法があり今回は そちらを利用したスレッドの待ち合わせの説明をします。
※ mutexはmutual exclusionの略らしいです。
ロック取得
ロック取得には2種類の方法があります。
- pthread_mutex_lock
- pthread_mutex_trylock
違いとしては上はロックされていればロックの解放まで処理は待ち合わせしますが 下の場合ロックされていればEBUSYが帰るような仕様です。
mutex_lockの使用例
#include <stdio.h> #include <stdlib.h> #include <err.h> #include <pthread.h> #include <unistd.h> #include <string.h> const size_t loop_max = 65535; pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; int counter = 0; void f1(); void f2(); int main(int argc, char *argv[]) { pthread_t thread1, thread2; int ret1,ret2; // スレッド生成 ret1 = pthread_create(&thread1,NULL,(void *)f1,NULL); ret2 = pthread_create(&thread2,NULL,(void *)f2,NULL); if (ret1 != 0) { err(EXIT_FAILURE, "can not create thread 1: %s", strerror(ret1) ); } if (ret2 != 0) { err(EXIT_FAILURE, "can not create thread 2: %s", strerror(ret2) ); } printf("execute pthread_join thread1\n"); ret1 = pthread_join(thread1,NULL); if (ret1 != 0) { errc(EXIT_FAILURE, ret1, "can not join thread 1"); } printf("execute pthread_join thread2\n"); ret2 = pthread_join(thread2,NULL); if (ret2 != 0) { errc(EXIT_FAILURE, ret2, "can not join thread 2"); } printf("done\n"); printf("%d\n", counter); pthread_mutex_destroy(&m); return 0; } void f1() { size_t i; for(i=0; i<loop_max; i++){ #ifndef NOLOCK int r; r = pthread_mutex_lock(&m); if (r != 0) { errc(EXIT_FAILURE, r, "can not lock"); } #endif counter++; #ifndef NOLOCK r = pthread_mutex_unlock(&m); if (r != 0) { errc(EXIT_FAILURE, r, "can not unlock"); } #endif } } void f2() { size_t i; for(i=0; i<loop_max; i++){ #ifndef NOLOCK if (pthread_mutex_lock(&m) != 0) { err(EXIT_FAILURE, "can not lock"); } #endif counter++; #ifndef NOLOCK if (pthread_mutex_unlock(&m) != 0) { err(EXIT_FAILURE, "can not unlock"); } #endif } }
$ gcc -lpthread mutex1.c -o mutex1
mutex_trylockの使用例
#include <stdio.h> #include <stdlib.h> #include <err.h> #include <errno.h> #include <pthread.h> #include <unistd.h> #include <string.h> const size_t loop_max = 65535; pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; int counter = 0; void f1(); void f2(); int main(int argc, char *argv[]) { pthread_t thread1, thread2; int ret1,ret2; // スレッド生成 ret1 = pthread_create(&thread1,NULL,(void *)f1,NULL); ret2 = pthread_create(&thread2,NULL,(void *)f2,NULL); if (ret1 != 0) { err(EXIT_FAILURE, "can not create thread 1: %s", strerror(ret1) ); } if (ret2 != 0) { err(EXIT_FAILURE, "can not create thread 2"); err(EXIT_FAILURE, "can not create thread 2: %s", strerror(ret2) ); } printf("execute pthread_join thread1\n"); ret1 = pthread_join(thread1,NULL); if (ret1 != 0) { errc(EXIT_FAILURE, ret1, "can not join thread 1"); } printf("execute pthread_join thread2\n"); ret2 = pthread_join(thread2,NULL); if (ret2 != 0) { errc(EXIT_FAILURE, ret2, "can not join thread 2"); } printf("done\n"); printf("%d\n", counter); pthread_mutex_destroy(&m); return 0; } void f1() { size_t i; size_t try_again = 0; for(i=0; i<loop_max; i++){ #ifndef NOLOCK int r; r = pthread_mutex_trylock(&m); if (r != 0) { if (EBUSY == r) { try_again++; continue; } errc(EXIT_FAILURE, r, "can not lock"); } #endif counter++; #ifndef NOLOCK r = pthread_mutex_unlock(&m); if (r != 0) { errc(EXIT_FAILURE, r, "can not unlock"); } #endif } printf("%s: try %lu\n", __func__, try_again); } void f2() { size_t i; size_t try_again = 0; for(i=0; i<loop_max; i++){ #ifndef NOLOCK int r; r = pthread_mutex_trylock(&m); if (r != 0) { if (EBUSY == r) { try_again++; continue; } err(EXIT_FAILURE, "can not lock"); } #endif counter++; #ifndef NOLOCK if (pthread_mutex_unlock(&m) != 0) { err(EXIT_FAILURE, "can not unlock"); } #endif } printf("%s: try %lu\n", __func__, try_again); }
$ gcc -lpthread mutex_try1.c -o mutex_try
まとめ
簡単にロックの例を書いてみました。 mutex関連は他にもたくさんの機能があるのでまだまだ学習が必要です。