記事ディレクトリ
1.スピンロック
1.1 サスペンド待ちとスピン待ち
最初に 2 つのシナリオを見てみましょう:
1️⃣ スレッドがロックを適用し、適用できないことがわかった場合、スレッドは他のことを行うようになります. この状態は、ハングおよび待機と呼ばれます. ウェイクアップする前にロックが解除されるのを待っています。
2️⃣ スレッドがロックを適用し、適用できないことが判明すると、ロックが解除されるかどうかを尋ね続けます。この状況をスピン待ちと呼びます。
しかし、待ち方は何によって決まるのでしょうか。
どれくらい待たなければならないかによります。つまり、クリティカル リソースに正常に適用されたスレッドがクリティカル セクションにとどまらなければならない時間。
では、どのように時間の長さを定義するのでしょうか?
使用シナリオを見ることができます. IO 操作が多い場合や特定のソフトウェア条件で待機している場合... 通常はハングして待機する必要があります. クリティカル セクションでのメモリ操作が非常に単純な場合は、通常、スピン待機です.
スピン待ちを使用しているときにデッドロックが発生すると、すべての実行フローが狂ってスピンし、CPU リソースがすぐに占有されるため、通常は suspend waiting を選択します。
また、どちらの待機方法を選択するかはプログラマ次第であるため、両方の方法を使用して効果をテストし、どちらが良いか悪いかを確認することもできます。
1.2 スピンロック インターフェイス
先ほど紹介したロック pthread_mutex とセマフォ sem は、保留待機のカテゴリに属します。
また、スピンロックにも独自のタイプがあります。
pthread_spinlock_t *lock
1.2.1 スピンロックの初期化と破棄
#include <pthread.h>
int pthread_spin_destroy(pthread_spinlock_t *lock);
int pthread_spin_init(pthread_spinlock_t *lock, int pshared);
1.2.2 スピンロックのロックとロック解除
ロック:
#include <pthread.h>
int pthread_spin_lock(pthread_spinlock_t *lock);
int pthread_spin_trylock(pthread_spinlock_t *lock);
ロックを解除:
#include <pthread.h>
int pthread_spin_unlock(pthread_spinlock_t *lock);
以前に使用したミューテックス ロックのインターフェイスが似ていることがわかります。実際にはミューテックスロックと実質的に同じであり、ユーザーの目には、ぶら下げ待機であろうと回転待機であろうと、待機していることになります。
2. リーダーライターモデル
2.1 シーン紹介
私たちは幼い頃、黒板新聞に絵を描いた経験があり、黒板新聞を描く人を作家、黒板新聞を読む人を読者と呼びました。
それらの関係について説明しましょう:
Writer と Writer:
作家と作家の関係は、報道が発生する可能性があるため、(異なる地域での描画状況に関係なく)典型的な相互排除関係です。
リーダーとライター:
書き手が書き終えていないとき、読み手はそれを読むかもしれないので、読み手は自分が何を読んだか分からないので、それらの間には相互に排他的な関係があるべきです。
作家が書き終えて、誰も読んで消して黒板を更新していないという状況もあるが、それはどういうことか。したがって、リーダーとライターの間には同期関係が必要です。
読者と読者:
リーダー と リーダー の間に関係はなく、相互排除や同期はありません。黒板新聞は誰でも読めるから。
これと、前に説明したブロッキング キューに基づく [Linux] プロデューサー/コンシューマー モデル (条件変数)との違いは、コンシューマーがデータを取得するが、リーダーは取得しないことです。
では、リーダー/ライターの問題はどのようなシナリオに当てはまるのでしょうか?
一度公開されると、長い間修正されることはなく、当ブログなどでほとんど読まれています。
2.2 読み書きロックのインターフェース
2.2.1 読み書きロックの初期化と破棄
#include <pthread.h>
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
const pthread_rwlockattr_t *restrict attr);
2.2.2 読み書きロックのロックとロック解除
リーダーロック:
#include <pthread.h>
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
ライターロック:
#include <pthread.h>
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
リーダーとライターのロックは異なるインターフェイスですが、ロック解除は 1 つのインターフェイスです。
ロックを解除:
#include <pthread.h>
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
2.3 擬似コードの原理
リーダーにはロックとカウンターがあります。
pthread_mutex_t rdlock;
int rdcnt = 0;
ライターには独自のロックもあります。
pthread_mutex_t wrlock;
リーダーがデータを読み取るとき:
lock(&rdlock);
rdcnt++;
if(rdcnt == 1)
{
// 不让写者进来
lock(&wrlock);
}
unlock(&rdlock);
// 读取数据……
lock(&rdlock);
rdcnt--;
if(rdcnt == 0)
{
// 现在可以让写者进入
unlock(&wrlock);
}
unlock(&rdlock);
ライターの書き込みプロセスははるかに簡単です。
lock(&wrlock);
// 写入
unlock(&wrlock);
一度に書き込むことができるライターは 1 人だけですが、複数のリーダーが読み取ることができます。
2.4 リーダー優先度とライター優先度
複数のリーダーがいる場合、ライターが入ることができず、ライターの空腹の問題が発生する可能性が非常に高く、これは正常です. 結局、リーダー/ライター モデルは、ほとんどの場合、読み取り状態になります。 、そしてこの状態を管理します。これをリーダー優先度と呼びます。
では、最初に作家はどうですか?
たとえば、現在 10 人のリーダーがいて、そのうち 5 つが既に入っている場合、ライターは、入っていない 5 つのスレッドを停止し、入ったスレッドの読み取りが終了するのを待ってから書き込むことができます。