BackEnd

mutexを使ってGoで排他処理をする

投稿日:

はじめに

Goで並列処理を行う際に複数のgoroutine間で同一の変数を扱いたい場合、宣言した変数に単純にアクセスするだけだと意図しない動作をする場合があります。

その場合、mutexを使って変数への排他制御を行うことで正しく動作させることができるので、mutexの使い方を紹介します。

mutexとは

mutexはsyncパッケージに定義されている構造体で、排他制御を行う際に用いられます。

あるgoroutine内でmutexのLockメソッドを実行すると、別のgoroutine内からはそのmutexに対して再度Lockメソッドを実行することはできず、mutexのUnlockメソッドが呼ばれるまで待機します。

mutexを扱う上での注意点として、mutexのLockメソッドを初めて実行した後は値をコピーしてはいけません。

mutexを使った排他制御

失敗するケース

最初に、mutexを使わずにある変数の値を並列で変更し、意図した値にならない場合のコードを紹介します。

16行目から21行目が非同期でcount1を加算している処理になります。

このように、count1に対してgoroutineの処理の中で値を加算していますが、このコードではcount1への同時アクセスが発生し、実行のたびに毎回値が変わり、殆どの場合は値が5000になりません。

これを正しく計算できるようにするために、mutexを使ってcount1変数へのアクセスを排他制御します。

mutexを使って排他制御した場合

count1への排他制御を行ったコードがこちらです。

まず、13行目でmutexを宣言しています。この変数に対して、goroutine内の23行目でロックを掛け、count1を加算したあとで28行目でアンロックをしています。

こうすることで、ロックを掛けたgoroutine以外がcount1にアクセスした場合、mutexがアンロックされるまで待機し、アンロックされてからロックを掛けることで、常にロックを掛けた1つのgoroutine以外はアクセスができなくなり、値が正しく計算されるようになります。

構造体へmutexを埋め込む

mutexを使って排他制御を行った例では、main関数内でmutexを定義していますが、多くのアプリケーションではmutexを構造体へ埋め込み、メソッド内でロック&アンロックを行うことが多いと思います。

ここでは、構造体へmutexを埋め込んだ例を紹介します。

まず、非同期での加算に対応したCounter構造体を定義します。

このように、IncremantメソッドとDecrementメソッドの開始時/終了時にmutexをロック/アンロックしています。

mutexのアンロックに関しては、このようにdeferで行うほうが漏れにくいので良いと思います。

次に、この構造体を使って非同期にカウントアップしたコードがこちらです。

mutexを構造体に埋め込んだ場合でも問題なく動作しました。

RWMutexを使う

RWMutexはmutexと同様に排他制御で用いられます。

両者の違いは、mutexがLockメソッドによってReadもWriteもブロックするのに対して、RWMutexにはLockメソッドとRLockメソッドの2種類のロック方法があり、RLockメソッドでロックした場合は、他のgoroutineからのRLockをブロックしないといった違いがあります。

これにより、Readの待ち時間を減らせる場合があります。

先程のCounter構造体のmutexをRWMutexに置き換えた場合の実装はこのようになります。

Counter構造体との違い、Valueメソッド内でのロックの取得がLockメソッドからRLockメソッドに変わっています。

これにより、あるgoroutineでValueメソッドを実行したとしても、別のgoroutineのValueメソッドの実行をブロックすることはなくなります(IncrementメソッドとDecrementメソッドの実行はブロックされます)。

そのため、値の更新を行わなずに値の取得のみを行うgoroutineの実行を高速化することができます。

さいごに

mutexを使った排他制御について紹介しました。mutexの内部実装について知りたい方に向けて参考になるURLを記載しますので、よろしければご覧ください。

https://speakerdeck.com/ffjlabo/sync-dot-mutexnoshi-zu-miwoli-jie-suru

おすすめ書籍

Go言語による並行処理 Go言語 100Tips ありがちなミスを把握し、実装を最適化する impress top gearシリーズ 効率的なGo ―データ指向によるGoアプリケーションの性能最適化

blog-page_footer_336




blog-page_footer_336




-BackEnd
-

執筆者:

免責事項

このブログは、記事上部に記載のある投稿日時点の一般的な情報を提供するものであり、投資等の勧誘・法的・税務上の助言を提供するものではありません。仮想通貨の投資・損益計算は複雑であり、個々の取引状況や法律の変更によって異なる可能性があります。ブログに記載された情報は参考程度のものであり、特定の状況に基づいた行動の決定には専門家の助言を求めることをお勧めします。当ブログの情報に基づいた行動に関連して生じた損失やリスクについて、筆者は責任を負いかねます。最新の法律や税務情報を確認し、必要に応じて専門家に相談することをお勧めします。


comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA


関連記事

Go言語

Golangのsyncパッケージによる同期・排他制御

1 はじめに2 sync.WaitGroup3 sync.Mutex4 sync.RWMutex5 sync.Map6 sync.Once7 sync.Pool8 さいごに9 おすすめ書籍 はじめに ...

rails

さらば「rails migrate」、よろしく「ridgepole」

1 はじめに2 Ridgepoleとは3 rails migrateではなく、Ridgepoleを選定した理由4 rails migrateからRidgepoleへの移行手順5 capistrano3 ...

Stripe Connectを使って複合プランの継続課金を実装

1 はじめに1.1 想定する継続課金1.2 想定するシチュエーション2 継続課金商品の作成2.1 プランの継続課金商品の作成2.2 ID数の継続課金商品の作成3 実装3.1 プランAとID数を30契約 ...

laravel logo

Laravelのバッチ処理を作る

1 はじめに2 環境3 artisanコマンド作成4 artisanコマンドをバッチとして登録する5 さいごに6 おすすめ書籍 はじめに こんにちは。webアプリにつきもののバッチ処理ですが、もちろん ...

Kotlinでのnullの基本的な扱いかた

1 はじめに2 基本的にnullを許容しない3 nullを許容するNullable4 Nullableをnon-nullに変える4.1 nullチェックとスマートキャスト4.2 エルビス演算子4.3 ...

フォロー

blog-page_side_responsive

2024年8月
 123
45678910
11121314151617
18192021222324
25262728293031

アプリ情報

私たちは無料アプリもリリースしています、ぜひご覧ください。 下記のアイコンから無料でダウンロードできます。