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


関連記事

laravel logo

[Laravel] middlewareでHTTPリクエストの前後に処理を入れる

1 はじめに2 middlewareとは3 実装3.1 middleware作成3.2 コード実装4 middleware定義4.1 常に呼び出す4.2 特定のURLにアクセスした時のみ呼び出す4.3 ...

Go言語

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

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

laravel logo

Laravelでテストコードを書くには? Featureテスト/Unitテスト

1 はじめに2 FeatureとUnitの使い分け3 テスト用データベースの準備4 Featureテスト4.1 テスト対象のコード4.2 テストコードの実装4.3 テスト結果の検証4.3.1 ステータ ...

rails

Rails Developer Meetup に参加してきました【1日目】

1 はじめに2 発表について2.1 安全かつ高速に進めるマイクロサービス化2.2 Rails in Microservices2.3 MySQL/InnoDB の裏側2.4 H2O/mruby でつく ...

php logo

PHPでGmail APIを利用してメールデータを取得してみる その2

1 はじめに2 メールの内容取得3 MessagePartオブジェクト3.1 件名3.2 本文4 multipartの場合4.1 本文の取得5 全文6 さいごに7 おすすめ書籍 はじめに 前回は、Gm ...

フォロー

blog-page_side_responsive

2024年8月
 123
45678910
11121314151617
18192021222324
25262728293031

アプリ情報

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