C LANGUAGE TECHNOLOGY

【C言語】POSIXスレッドのRead-Write Lockの使い方

悩んでいる人

C言語でPOSIXスレッドのRead-Write Lockの使い方を教えて!

こういった悩みにお答えします.

本記事の信頼性

  • リアルタイムシステムの研究歴12年.
  • 東大教員の時に,英語でOSの授業.
  • 2012年9月~2013年8月にアメリカのノースカロライナ大学チャペルヒル校コンピュータサイエンス学部2021年の世界大学学術ランキングで20位)で客員研究員として勤務.C言語でリアルタイムLinuxの研究開発
  • プログラミング歴15年以上,習得している言語: C/C++Solidity/Vyper,Java,Python,Ruby,HTML/CSS/JS/PHP,MATLAB,Assembler (x64,ARM).
  • 東大教員の時に,C++言語で開発した「LLVMコンパイラの拡張」,C言語で開発した独自のリアルタイムOS「Mcube Kernel」GitHubにオープンソースとして公開

こういった私から学べます.

本記事ではスレッド,プロセス,ミューテックス,セマフォを理解している前提で説明しますので,これらを学びたいあなたはこちらからどうぞ.

C言語 スレッド
【C言語】スレッドの生成と実行【pthread,マルチスレッド,スレッドIDの取得,C11規格のスレッド】

こういった悩みにお答えします. こういった私から学べます. 目次1 スレッド2 pthreadによるマルチスレッドプログラミング3 pthreadでスレッドIDの取得3.1 pthread_self関 ...

続きを見る

C言語 プロセス
【C言語】プロセスの生成と実行【fork/wait/execve/getpid/getppid関数】

こういった悩みにお答えします. こういった私から学べます. 目次1 プロセス2 fork関数でプロセスの生成,wait関数で子プロセスの終了まで待機3 execve関数でコマンドの実行4 getpid ...

続きを見る

C言語 ミューテックス
【C言語】ミューテックスとは

こういった悩みにお答えします. こういった私から学べます. 本記事ではスレッドとプロセスを理解している前提で説明します. スレッドとプロセスを学びたいあなたはこちらからどうぞ. 目次1 ミューテックス ...

続きを見る

C言語 セマフォ
【C言語】セマフォとは

こういった悩みにお答えします. こういった私から学べます. 本記事ではスレッドとプロセスを理解している前提で説明します. スレッドとプロセスを学びたいあなたはこちらからどうぞ. 目次1 セマフォ2 P ...

続きを見る

POSIXスレッドのRead-Write Lock

POSIXスレッドのRead-Write Lockを紹介します.

Read-Write Lockとは,readers-writers問題の1つを解決する同期プリミティブです.

Read-Write Lockは,読み取り専用操作では同時アクセスが可能ですが,書き込み操作では排他的アクセスが必要です.

つまり,複数のスレッドが並行してデータを読むことができますが,データの書き込みや変更には排他的ロックが必要になります.

書き手がデータを書き込んでいるときは,書き手が書き込みを終えるまで,他のすべての書き手や読み手がブロックされます.

一般的な使用例としては,アトミックに更新できないメモリ上のデータ構造へのアクセスを制御し,更新が完了するまで無効とする(他のスレッドから読んではいけない)ような場合があります.

Read-Write Lockは通常,ミューテックスと条件変数の上,あるいはセマフォの上に構築されます.

pthread_rwlock_init/pthread_rwlock_destroy関数

pthread_rwlock_init/pthread_rwlock_destroy関数を紹介します.

pthread_rwlock_init関数は,rwlockが参照するRead-Write Lockを使用するために必要なリソースを割り当てて,attrが参照する属性を持つロックをアンロック状態に初期化します.

attrがNULLの場合,デフォルトの読み書きロック属性が使用され,デフォルトの読み書きロック属性のオブジェクトのアドレスが渡されるのと同じ効果があります.

一度初期化されたロックは,再初期化されることなく何度でも使用することができます.

pthread_rwlock_destroy関数は,rwlockによって参照される読み書きロックオブジェクトを破棄し,ロックによって使用されるリソースを解放します.

pthread_rwlock_init/pthread_rwlock_destroy関数は,成功した場合は0を返します.

pthread_rwlockattr_init/pthread_rwlockattr_destroy関数

pthread_rwlockattr_init/pthread_rwlockattr_destroy関数を紹介します.

pthread_rwlockattr_init関数は,Read/Write Lock属性オブジェクトattrを実装によって定義されたすべての属性のデフォルト値で初期化します.

pthread_rwlockattr_destroy関数は,Read/Write Lock属性オブジェクトを破棄します.

pthread_rwlock_init/pthread_rwlock_destroy関数は,成功した場合は0を返します.

pthread_rwlockattr_getpshared/pthread_rwlockattr_setpshared関数

pthread_rwlockattr_getpshared/pthread_rwlockattr_setpshared関数を紹介します.

pthread_rwlockattr_getpshared関数は,attrが参照する初期化済み属性オブジェクトからプロセス共有属性の値を取得します.

pthread_rwlockattr_setpshared関数は,attrが参照する初期化済み属性オブジェクトにプロセス共有属性を設定します.

pthread_rwlockattr_getpshared/pthread_rwlockattr_setpshared関数は,成功した場合は0を返します.

pthread_rwlock_rdlock/pthread_rwlock_tryrdlock関数

pthread_rwlock_rdlock/pthread_rwlock_tryrdlock関数を紹介します.

pthread_rwlock_rdlock関数は,rwlockが参照するRead-Write LockにRead Lockを適用します.

呼び出し側のスレッドは,ライターがロックを保持しておらず,かつロック上でブロックされているライターが存在しない場合,Read Lockを取得します.

スレッドは,rwlockに対して複数の同時読み取りロックを保持することができます.

つまり,pthread_rwlock_rdlock関数をn回正常に呼び出すことができます.

この場合,アプリケーションは,そのスレッドが一致するロック解除を行う(すなわち,pthread_rwlock_unlock関数をn回呼び出す)ことを保証しなければなりません.

pthread_rwlock_tryrdlock関数は,pthread_rwlock_rdlock関数と同様に読み込みロックをかけます.

ただし,同等のpthread_rwlock_rdlock関数が呼び出したスレッドをブロックしていた場合は失敗するという違いがあります.

pthread_rwlock_tryrdlock関数は,いかなる場合にもブロックしてはなりません.

pthread_rwlock_rdlock/pthread_rwlock_tryrdlock関数は,成功した場合は0を返します.

pthread_rwlock_wrlock/pthread_rwlock_trywrlock関数

pthread_rwlock_wrlock/pthread_rwlock_trywrlock関数を紹介します.

pthread_rwlock_wrlock関数は,rwlockが参照するRead-Write LockにWrite Lockを適用します.

他のスレッド(readerまたはwriter)がRead-Write Lockのrwlockを保持していない場合,呼び出したスレッドはWrite Lockを取得します.

pthread_rwlock_trywrlock関数は,pthread_rwlock_wrlock関数と同様にWrite Lockをかけるが,現在rwlockを(読み込みまたは書き込みのために)持っているスレッドがある場合は失敗することを除けば,この関数もWrite Lockをかけます.

そうでない場合,そのスレッドは,ロックを獲得できるまでブロックしなければなりません.

呼び出しが行われた時点で,Read LockまたはWrite Lockを保持している場合,呼び出し側のスレッドはデッドロックする可能性があります.

pthread_rwlock_wrlock/pthread_rwlock_trywrlock関数は,成功した場合は0を返します.

pthread_rwlock_unlock関数

pthread_rwlock_unlock関数は,rwlockが参照するRead-Write Lockオブジェクトに保持されているロックを解放しなければなりません.

Read-Write Lockオブジェクトからreadロックを解放するために呼ばれます.

Read-Write Lockオブジェクトに他のRead Lockが現在保持されている場合,Read-Write LockオブジェクトはRead Lock状態のままです.

Read-Write Lockオブジェクトに対する最後のRead Lockを解放する場合,Read-Write Lockオブジェクトは,所有者を持たないunlocked状態に置かれます.

Read-Write Lockオブジェクトに対するWrite Lockを解放するために呼ばれた場合,Read-Write Lockオブジェクトは,unlocked状態に置かれなければなりません.

POSIXスレッドのRead-Write Lockをスレッド間で利用

スレッド間のロックにpthread_rwlock_rdlock/pthread_rwlock_wrlock関数を利用

スレッド間のロックにpthread_rwlock_rdlock/pthread_rwlock_wrlock関数を利用するコードは以下になります.

実行結果は以下になります.

countの数値がLOOP_NUM(=50000)の2倍,つまり50000 * 2 = 100000になることがわかります.

-DCOUNTUPオプションを付けてコンパイルした場合の実行結果は以下になります.

countの値が正常に100000にインクリメントされる様子がわかります.

-DNO_LOCKオプションを付けてコンパイルした場合の実行結果は以下になります.

5行目の「count = 68359」で100000より小さい値になっていることがわかります.(100000や他の値になる場合もあります.)

この理由は,Linuxはプリエンプティブ・マルチタスクの実行が可能なため,countのインクリメント処理でインターリーブが発生してしまうからです.

-DCOUNTUPと-DNO_LOCKオプションを付けてコンパイルした場合の実行結果は以下になります.

以下の例では「count = 99979」で100000より小さい値になっていることがわかります.(100000や他の値になる場合もあります.)

スレッド間のロックにpthread_rwlock_tryrdlock/pthread_rwlock_trywrlock関数を利用

スレッド間のロックにpthread_rwlock_tryrdlock/pthread_rwlock_trywrlock関数を利用するコードは以下になります.

実行結果は以下になります.

POSIXスレッドのRead-Write Lockをプロセス間で利用

プロセス間のロックにpthread_rwlock_rdlock/pthread_rwlock_wrlock関数を利用

プロセス間のロックにpthread_rwlock_rdlock/pthread_rwlock_wrlock関数を利用するコードは以下になります.

実行結果は以下になります.

shm_open/shm_unlink関数を利用する際は-lrtオプションが必要なことに注意して下さい.

6行目の「*count = 100000」で正常にカウントできていることがわかります.

※5行目の*countは先に実行終了した親プロセスまたは子プロセスの表示です.

プロセス間のロックにpthread_rwlock_tryrdlock/pthread_rwlock_trywrlock関数を利用

プロセス間のロックにpthread_rwlock_tryrdlock/pthread_rwlock_trywrlock関数を利用するコードは以下になります.

実行結果は以下になります.

まとめ

C言語でPOSIXスレッドのRead-Write Lockの使い方を紹介しました.

Read-Write Lockの概念は結構難しいので,何度も読み直しましょう!

C言語を独学で習得することは難しいです.

私にC言語の無料相談をしたいあなたは,公式LINE「ChishiroのC言語」の友だち追加をお願い致します.

友だち追加

独学が難しいあなたは,元東大教員がおすすめするC言語を学べるオンラインプログラミングスクール5社で自分に合うスクールを見つけましょう.後悔はさせません!

-C LANGUAGE, TECHNOLOGY
-, , , , ,