SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

pthreadについて

pthreadについて(同期)

pthreadの同期方法


3.同期(3/4):read/write lock

3.5 read/write lock

 スレッドが保護対象を参照する時、変更しようとしているスレッドはロックされていないといけませんが、参照しようとしている他のスレッドはロックされていなくてもいいはずです。逆に変更しようとしているスレッドが複数居ても、各スレッドでロックされていないといけません。

 mutexやspinでは、保護対象の情報に対して上記のような柔軟性を持っておらず、全スレッドで排他されてしまい、ひいてはボトルネックになります。

 キャッシュ情報へのアクセス等は情報の参照だけであれば複数スレッドによる同時処理でも問題ないはず。それを実現するのが、read/write lockです。

  • int pthread_rwlock_init( pthread_rwlock_t * __rwlock, __const pthread_rwlockattr_t * __attr )
  • int pthread_rwlock_destroy( pthread_rwlock_t * __rwlock )
  • int pthread_rwlock_rdlock( pthread_rwlock_t * __rwlock )
  • int pthread_rwlock_tryrdlock( pthread_rwlock_t * __rwlock )
  • int pthread_rwlock_timedrdlock( pthread_rwlock_t * __rwlock, __const struct timespec * __abstime )
  • int pthread_rwlock_wrlock( pthread_rwlock_t * __rwlock )
  • int pthread_rwlock_trywrlock( pthread_rwlock_t * __rwlock )
  • int pthread_rwlock_timedwrlock( pthread_rwlock_t * __rwlock, __const struct timespec * __abstime )
  • int pthread_rwlock_unlock( pthread_rwlock_t * __rwlock )

 pthread_rwlockattr_t * __attrはspinと同様、プロセスをまたいで使用するかの属性を設定します。使用しない場合はNULLを設定し、自プロセス内のスレッドにのみ使用する設定になります。

 下記サンプルプログラムを動かします。write_funcはwrite_countを変更し、read_funcはwrite_countを参照しています。read_funcのカウンタは高速で進みますが、write_funcは1秒毎でwrite_lockします。write_lockされるとread_funcのカウンタが止まり、write_funcがunlockすると、read_funcが再開しますが、カウンタの末尾のアルファベットが変更されています。

 なお、write_funcはread_funcの生存確認をpthread_killで行ってますが、この関数の説明は別称で行います。ちなみにスレッド生成の際、怠けて数値を直接変数として設定しているため、ワーニングが出ます。手を抜いてすいません。

rwlock_test.c
/* gcc rwlock_test.c -o rwlock_test -g -W -Wall -lpthread */
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <signal.h>
#define LOOP_CNT 20000000
pthread_rwlock_t lock;
int write_count = 0x41;  /* 'A' */
pthread_t id[4];
void * read_func ( void * arg );
void * write_func( void * arg );

int main( ) {

    pthread_rwlock_init( &lock, 0 );

    pthread_create( &id[0], 0, read_func,  ( void * )0 );
    pthread_create( &id[1], 0, read_func,  ( void * )1 );
    pthread_create( &id[2], 0, read_func,  ( void * )2 );
    pthread_create( &id[3], 0, write_func, ( void * )3 );
    pthread_join( id[0], 0 );
    pthread_join( id[1], 0 );
    pthread_join( id[2], 0 );
    pthread_join( id[3], 0 );

    pthread_rwlock_destroy( &lock );
    return 0;
}

void * read_func( void * arg ) {
    int index = ( int * )arg;
    int count = 0;
    int i;
    for( i = 0; i < LOOP_CNT; i ++ ) {
        pthread_rwlock_rdlock( &lock );
        count ++;
        if( count % 1000 == 0 ) {
            printf( "\t\t%d %*.*s%5d[%c]\r",
                index, index + 1, index + 1,
                "\t\t\t", count / 1000, write_count );
        }
        pthread_rwlock_unlock( &lock );
    }
    printf( "thread %d End!!\n", index );
    return 0;
}

void * write_func( void * arg ) {
    int index = ( int * )arg;
    int i;
    for( i = 0; ; i ++ ) {
        sleep( 1 );
        if( pthread_kill( id[0], 0 )
        &&  pthread_kill( id[1], 0 )
        &&  pthread_kill( id[2], 0 )) {
            break;
        }
        pthread_rwlock_wrlock( &lock );
        printf( "\nnow wrlock!! %c -> %c\n",
            write_count, write_count + 1 );
        write_count ++;
        sleep( 1 );
        pthread_rwlock_unlock( &lock );
    }
    printf( "thread %d End!!\n", index );
    return 0;
}

 尚、read/write lockもそれ程難しくなく自作できます。

 init/read lock/read unlock/write lock/write unlock を自作しました。プログラムが多少長いので詳細はダウンロードファイル(wrap_rwlock.c)を参照願います。詳細は下記のような感じです。

  • read_lock
  • write中かwrite待ちのスレッドが居る場合はサスペンドします。
  • read_unlock
  • read中が居ない且つwrite待ちが居る場合はwrite待ちスレッドにシグナルを発行します。
  • write_lock
  • write中かread中が居る場合はサスペンドします。
  • write_unlock
  • write待ちが居ればwrite待ちスレッドに、write待ちが居なくてread待ちが居る場合はread待ちスレッドにシグナルを発行します。

 実際にプログラム(wrap_rwlock.c)を動かした結果もpthread_rwlock_*系関数と同じ動きになります。destroyやtrylockの自作を試しても良いと思います。

次のページ
3.同期(4/4):性能

この記事は参考になりましたか?

pthreadについて連載記事一覧

もっと読む

この記事の著者

赤松 エイト(エイト)

(株)DTSに勤てます。WebアプリやJavaやLL等の上位アプリ環境を密かに憧れつつも、ず~っとLinuxとかHP-UXばかり、ここ数年はカーネル以上アプリ未満のあたりを行ったり来たりしています。mixiもやってまして、こちらは子育てとか日々の日記メインです。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/1893 2008/08/20 15:59

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー