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の基礎知識

1 はじめに2 Laravelの概要2.1 学習コストが低い2.2 Symfonyがベース2.3 DIを積極的に活用している3 ディレクトリ構成3.1 app3.2 bootstrap3.3 conf ...

WebアプリケーションにLINEログインを組み込む

1 はじめに2 LINEログインとは2.1 LINEログインでできること2.2 LINEログインのフロー3 LINEログインの設定3.1 プロバイダーを新規作成する3.2 チャネルを作成する3.3 リ ...

Go言語

Go言語でGinkgoを利用してBDDしてみた

1 はじめに2 BDDとは3 Ginkgoについて3.1 Ginkgoの概要3.1.1 Describe3.1.2 Context3.1.3 It3.1.4 JustBeforeEach3.1.5 B ...

rails

deviseを使ってユーザ登録フォームを作る

1 はじめに1.1 前提条件2 deviseについて2.1 deviseとは2.2 deviseでできること3 登録処理の実装3.1 Gemのインストール3.2 deviseのインストール3.3 デフ ...

aws

ALB+EC2な環境でhttpをhttpsにリダイレクトする

1 はじめに1.1 前提条件2 ALBの設定3 Nginxの設定3.1 注意点4 さいごに はじめに httpsに対応済みのWebサイトの場合、httpでアクセスされた時にhttpsでリダイレクトする ...

フォロー

blog-page_side_responsive

2024年8月
 123
45678910
11121314151617
18192021222324
25262728293031

アプリ情報

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