ホームページ  >  記事  >  バックエンド開発  >  Golang のロック実装を研究する

Golang のロック実装を研究する

PHPz
PHPzオリジナル
2023-12-28 10:32:43664ブラウズ

Golang のロック実装を研究する

Golang ロックの実装メカニズムの探索

はじめに:

同時プログラミングでは、ロック (Lock) が一般的に使用される同期メカニズムです。共有リソースへのアクセス。 Golang は、高い同時実行パフォーマンスと簡潔な構文を備えたプログラミング言語として、ミューテックス (Mutex)、読み取り/書き込みロック (RWMutex) などを含む豊富なロック メカニズムを提供します。この記事では、Golang ロックの実装メカニズムを詳しく掘り下げ、特定のコード例を通じてそれを示します。

1. ミューテックス ロック (Mutex) の実装メカニズム

  1. ロック メソッドの実装:

ミューテックス ロックの実装メカニズムは、主に 3 つの重要な方法によって行われます。コンポーネント: 待機キュー、ステータス フラグ、およびアトミック操作。スレッドがミューテックス ロックを取得しようとすると、まずステータス フラグをチェックし、ステータス フラグがロックされている場合は、スレッド自体を待機キューに追加してスピンして待機します。ステータス フラグがロック解除されている場合は、アトミック操作を使用してロックを取得し、ステータス フラグをロックに設定してみます。次に、ミューテックス ロックの具体的なコード例を示します。

type Mutex struct {
    waiting   int32 // 等待队列,记录等待获取锁的goroutine数量
    isLocked  int32 // 锁的状态标志,0代表未锁住,1代表已锁住
}

func (m *Mutex) Lock() {
    for !atomic.CompareAndSwapInt32(&m.isLocked, 0, 1) { // 自旋等待获取锁
        runtime.Gosched()
    }
}

func (m *Mutex) Unlock() {
    atomic.StoreInt32(&m.isLocked, 0) // 释放锁,将状态标志设置为未锁住
}
  1. アトミック操作の実装:

上記のコードは、アトミック パッケージの CompareAndSwapInt32 関数と StoreInt32 関数を使用して実装します。アトミック操作。 CompareAndSwapInt32 関数は、比較および交換操作に使用されます。ロックのステータス フラグがロックされていない場合は、locked に設定され、true を返します。ロックのステータス フラグがロックされている場合は、false を返します。 StoreInt32 関数は、ステータス フラグをアトミックにロック解除に設定するために使用されます。これらのアトミックな操作により、競合状態の発生を効果的に回避し、ロックの正確性を確保できます。

2. 読み取り/書き込みロック (RWMutex) の実装メカニズム

  1. 書き込みロックの実装メカニズム:

読み取り/書き込みロックは次のとおりです。特別な種類のロック メカニズム。複数の goroutine が共有リソースを同時に読み取ることを許可しますが、共有リソースに書き込むことができるのは 1 つの goroutine のみです。書き込みロックの実装メカニズムはミューテックス ロックの実装メカニズムと似ていますが、いくつかの違いがあります。以下は、書き込みロックの具体的なコード例です:

type RWMutex struct {
    writerSem uint32    // 写入信号量,用于限制只能有一个goroutine写入
    readerSem uint32    // 读取信号量,用于限制多个goroutine同时读取
    readerCount int32   // 读取计数,记录当前同时读取的goroutine数量
    readerWait  int32   // 当前等待读取的goroutine数量
}

func (rw *RWMutex) Lock() {
    rw.lockWhile(func() {atomic.LoadUint32(&rw.readerSem) != 0 || atomic.LoadUint32(&rw.writerSem) != 0})
    atomic.AddUint32(&rw.writerSem, 1) // 获取写锁,递增写入信号量
}

func (rw *RWMutex) Unlock() {
    atomic.AddUint32(&rw.writerSem, ^uint32(0)) // 释放写锁,递减写入信号量
    rw.unlockWhile(func() {atomic.LoadInt32(&rw.readerCount) != 0}) // 释放读锁,根据读取计数判断是否需要唤醒等待读取的goroutine
}
  1. 読み取りロックの実装メカニズム:

読み取りロックの実装メカニズムは、主に読み取りセマフォのインクリメントによって行われます。これを実現するには、ゴルーチンが読み取りロックを取得するときに、最初に書き込みセマフォがゼロかどうか、書き込みを待機している他のゴルーチンがないかどうかを確認し、そうであれば、読み取りカウントをインクリメントして読み取りロックを取得します。それ以外の場合は、待機キューに自身を追加します。スピン待機を実行します。以下は、読み取りロックの具体的なコード例です:

func (rw *RWMutex) RLock() {
    rw.lockWhile(func() {atomic.LoadUint32(&rw.writerSem) != 0}) // 当有 goroutine 持有写锁时,自旋等待
    atomic.AddInt32(&rw.readerCount, 1) // 递增读取计数
}

func (rw *RWMutex) RUnlock() {
    atomic.AddInt32(&rw.readerCount, -1) // 递减读取计数
    rw.unlockWhile(func() {atomic.LoadInt32(&rw.readerCount) != 0}) // 根据读取计数判断是否需要唤醒等待读取的goroutine
}
  1. 待機中の goroutine を起動します:

読み取り/書き込みロックの実装には、待機中のゴルーチンを起動する操作。これは、lockwhile と lockwhile という 2 つの補助関数によって実装されます。 lockwhile 関数はスピン待機に使用されます。指定された条件が true の場合、ゴルーチンは条件が満たされるまでブロックされます。unlockwhile 関数は、指定された条件に従って待機中のゴルーチンをウェイクアップして、ロック。これにより、ロックを待機しているゴルーチンが時間内に起動できるようになり、同時実行パフォーマンスが向上します。

概要:

この記事では、Golang のロック実装メカニズムを徹底的に調査し、特定のコード例を通じてそれを実証しました。ミューテックス ロックは待機キューとステータス フラグを通じて実装され、1 つのゴルーチンのみがロックを保持できるようにします。一方、読み取り/書き込みロックは書き込みセマフォ、読み取りセマフォ、読み取りカウントを通じて実装され、複数のゴルーチンが同時に読み書きできるようになります。書き込みを許可される goroutine は 1 つだけです。これらのロック メカニズムにより、共有リソースへの安全なアクセスが確保され、アトミックな操作と条件付き待機を通じて同時プログラムのパフォーマンスが向上します。

以上がGolang のロック実装を研究するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。