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アプリケーションの性能最適化

page_footer_responsive




-BackEnd
-

執筆者:

免責事項

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


comment

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

CAPTCHA


関連記事

Go言語

Go言語の基礎〜Go 1.11 開発環境構築とパッケージバージョン管理〜

1 はじめに2 Go言語(Golang)とは2.1 シンプルな構文2.2 コンパイル言語2.3 並行処理2.4 その他の特徴3 Go開発環境の構築3.1 Goのインストール3.1.1 1. homeb ...

rails

Shrineを使って画像をアップロードする

1 はじめに2 Shrineとは2.1 簡単な説明2.2 作者2.3 特徴3 下準備3.1 Gemを追加3.2 初期設定3.3 テーブルを作成する4 実装4.1 Uploaderの実装4.2 Mode ...

Go言語

sqlcを使ってSQLファイルからGoのコードを生成する

1 はじめに2 sqlcとは2.1 コード解析2.2 サポートする言語とデータベース3 sqlcでコードを生成する3.1 準備3.2 DDLを書く3.3 クエリを書く3.4 コードを生成する3.5 生 ...

Go言語

Go 1.23 で追加された iter パッケージを使ってみる

1 はじめに1.1 iteratorとは2 push方式のイテレータ2.1 kとvを受け取るイテレータ関数2.2 イテレータ関数をチェインさせる2.3 イテレータ関数の型を利用する2.4 イテレーショ ...

Rubyを使ってDialogflowのお勉強

1 はじめに1.1 環境2 とりあえずAPIで叩いてみる2.1 テスト用のIntentを作成2.2 Gemの設定3 Eventについて3.1 WELCOMEイベント3.2 パラメータ付きで叩く3.3 ...

フォロー

blog-page_side_responsive

2024年8月
 123
45678910
11121314151617
18192021222324
25262728293031

アプリ情報

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