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


関連記事

Stripe Connectを使ってCheckoutを利用した継続課金を実装

1 はじめに1.1 Checkoutを使う場合の動線2 決済画面への遷移2.1 マイグレーション2.2 Checkout Sessionの作成3 決済完了後の制御4 おまけ4.1 ローカルでWebho ...

Go言語

Goのクエリビルダー goqu を使ってみる

1 はじめに2 goquとは2.1 対応するDB3 基本的な使い方3.1 Insert句を生成する3.2 Select句を生成する3.3 Update句を生成する3.4 Delete句を生成する4 よ ...

Go言語

Go 1.19でGoDocに追加された機能

1 はじめに2 GoDocの機能追加2.1 セクションタイトル(Headings)2.2 リンク2.3 リスト2.4 パッケージの追加3 おまけ4 さいごに5 おすすめ書籍 はじめに 8月2日にGo ...

Go言語

GoでStructのAccessorを自動生成する

1 はじめに2 Accessorを自動生成する2.1 基本的な使い方2.2 receiver変数を変更する場合2.3 生成するファイル名を変える場合2.4 排他制御を行い場合3 どのように生成している ...

rails

RailsでERBからJavaScriptにhashを渡す方法

1 はじめに2 カスタムデータ属性とは3 実装例3.1 コントローラの実装3.2 ビューの実装3.3 実行結果4 さいごに はじめに 以前、選択したプルダウンメニューに応じて別のプルダウンメニューの内 ...

フォロー

blog-page_side_responsive

2024年8月
 123
45678910
11121314151617
18192021222324
25262728293031

アプリ情報

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