ホームページ  >  記事  >  バックエンド開発  >  Golang ミューテックスの基礎となる実装

Golang ミューテックスの基礎となる実装

WBOY
WBOYオリジナル
2023-05-10 19:04:05728ブラウズ

Golang は効率的で簡潔なプログラミング言語であり、その優れたパフォーマンスと使いやすさは同時プログラミングのアプリケーションに反映されています。同時プログラミングでは、Mutex は非常に一般的な同期メカニズムであり、競合状態 (つまり、複数のスレッドが共有リソースに同時にアクセスした場合の予測できない結果) を回避しながら、マルチスレッド環境の共有リソースへの相互排他的アクセスを保証できます。この記事では、Mutex の基本的な実装原理を紹介します。

1. Mutex の定義

Golang では、Mutex は共有リソースへの相互排他的アクセスを保護するために使用される同期メカニズムです。これには、Lock() と Unlock() という 2 つのメソッドが含まれており、それぞれ Mutex をロックおよびロック解除するために使用されます。スレッドがミューテックスのロックを取得すると、ロックが解放されるまで他のスレッドはブロックされます。

2. Mutex の基礎となる実装

Golang では、Mutex の基礎となる実装は主に sync.Mutex と呼ばれる構造に依存します。 Mutex の実装は、基盤となるハードウェアのアトミックな操作に依存する CAS 操作 (Compare-And-Swap) を通じて実装されます。

ミューテックス構造は次のように定義されます:

type Mutex struct {
    state int32
    sema  *uint32 // 信号量
}

const (
    mutexLocked = 1 << iota // mutex is locked
)

func (m *Mutex) Lock() {
    // Fast path: 这里如果加锁成功,就直接返回
    if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
        return
    }
    // Slow path : 防止出现busy spinning,将当前协程加入等待队列,然后调用runtime.semacquire继续sleep。
    sema := m.sema
    if sema == nil {
        sema = new(uint32)
        m.sema = sema
    }
    runtime_Semacquire(sema)
}

func (m *Mutex) Unlock() {
    // Fast path: 这里如果释放锁成功,就直接返回
    if atomic.CompareAndSwapInt32(&m.state, mutexLocked, 0) {
        return
    }
    // Slow path: 如果锁还被持有,则调用sync.runtime_Semrelease继续唤醒协程。
    sema := m.sema
    if sema == nil {
        panic("sync: unlock of unlocked mutex")
    }
    runtime_Semrelease(sema, false, 1)
}

ミューテックス構造には、状態とセマフォ sema の 2 つのフィールドが含まれます。このうち、stateはロックの状態を示し、mutexLockedはロックされていることを示し、その他の値はロックされていないことを示します。 sema は、ロックを待機しているゴルーチンを調整するために使用されます。

Mutex.Lock() メソッドでは、現在の Mutex がロックされていない場合、CompareAndSwapInt32 アトミック操作を使用して状態を 0 から mutexLocked に変更し、成功後に直接戻ります。そうでない場合は、現在のゴルーチンを結合させます。待機中のキューを呼び出し、 runtime.semacquire() メソッドを呼び出してキューを起動します。 Mutex.Unlock() メソッドでは、現在の Mutex がロックされている場合、CompareAndSwapInt32 アトミック操作を使用して状態を mutexLocked から 0 に変更し、成功後に直接戻ります。それ以外の場合は、現在の Mutex がロックされていないことを示す例外がスローされます。ロックされた。

Mutex.Lock() メソッドと Mutex.Unlock() メソッドの両方に高速パスと低速パスがあります。ファスト パスとは、ロック状態が占有されていないときに、CAS を介してロックをすぐに取得したり、ロックをすぐに解放したりできることを意味します。遅いパスは、ロック ステータスが占有されている場合、現在の goroutine を待機キューに追加する必要があり、 sema.acquire() メソッドを呼び出してスリープさせるか、待機キュー内の他の goroutine を起動する必要があることを意味します。

3. Mutex の使用に関する誤解

Golang では、Mutex は非常に一般的に使用される同期メカニズムですが、使用中に避けるべきよくある誤解がいくつかあります。

  1. Unlock() メソッドを呼び出すゴルーチンは、ミューテックスを保持するゴルーチンである必要があります。

ゴルーチンが保持していないミューテックスを解放しようとすると、プログラムはパニックを起こします。ミューテックス ロックは、共有リソースへの相互排他的アクセスを保証するためのものであり、ゴルーチンがロックを解放できる場合、その相互排他性は保証されません。

  1. Mutex はコピーしないでください

Mutex ロックはポインタ型であるため、ロック変数をコピーする必要がある場合は、ポインタ コピーを使用する必要があります。そうしないと、無関係な 2 つの Mutex インスタンスが同じ状態を共有することになり、予期しない動作が発生する可能性があります。

  1. ロックの自由な競合を避けるようにしてください。

同時プログラミングでは、ロックの自由な競合は、1 つの goroutine がロックを解放する前に、別の goroutine がロックの取得を試行し続けることを意味します。待機キューで待機する代わりにロックを解除します。これは CPU リソースの無駄遣いにつながるため、この状況はできる限り回避する必要があります。

つまり、ロックは共有リソースを保護するための強力なツールであり、同時プログラミングにおいて非常に重要な役割を果たします。この記事を通じて、Mutex の基本的な実装原理と、Mutex を使用する際に注意する必要があるいくつかの誤解について理解しました。実際の開発では、同時プログラミングで発生する可能性のあるさまざまな問題を回避するために、Mutex の利点を最大限に活用する必要があります。

以上がGolang ミューテックスの基礎となる実装の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

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