地方エンジニアの学習日記

興味ある技術の雑なメモだったりを書いてくブログ。たまに日記とガジェット紹介。

C言語 排他ロックする方法(mutex)

f:id:ryuichi1208:20190113230131j:plain

マルチスレッドプログラミングでスレッド間で共有データにアクセスするときに、 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関連は他にもたくさんの機能があるのでまだまだ学習が必要です。