1. ホーム
  2. スクリプト・コラム
  3. ゴラン

同時進行のプログラミングを行う sync.Once

2022-02-14 12:22:53

sync.Once ある動作が一度だけ実行されることを保証するために使用され、設定の初期化などシングルインスタンスパターンで使用することができる。init() 関数も一度だけ実行されることが分かっていますが、これは main() 関数を使用します。コード実行中に一度だけアクションを実行したい場合は sync.Once で、その使い方を説明します。

まず、次のコードから始めましょう。

package main

import (
 "fmt"
 "sync"
)


func main() {
 var num = 6
 var once sync.Once

 add_one := func() {
  num = num + 1
 }

 minus_one := func() {
  num = num - 1
 } 

 once.Do(add_one)
 fmt.Printf("The num: %d\n", num)
 once.Do(minus_one)
 fmt.Printf("The num: %d\n", num)
}


実装結果です。

数字:7
数字:7

sync.Once 型は Do メソッドは、1つのパラメータのみを受け入れ、型は func() は、引数宣言も結果宣言もない関数です。

Do メソッドは、最初に呼び出されたときに渡された関数を一度だけ実行し、他の関数は実行しません。上の例では、渡された関数が異なっていても、最初に渡された関数のみが実行されます。もし一度だけ実行される関数が複数ある場合は、それぞれの関数に sync.Once 型の値

func main() {
 var num = 6
 var once1 sync.Once
 var once2 sync.

 add_one := func() {
  num = num + 1
 }

 minus_one := func() {
  num = num - 1
 } 

 once1.Do(add_one)
 fmt.Printf("The num: %d\n", num)
 once2.Do(minus_one)
 fmt.Printf("The num: %d\n", num)
}



sync.Once 型は構造体型であり、その名称は done uint32 型フィールドと、相互に排他的なロック m .

type Once struct {
 done uint32
 m Mutex
m Mutex}



done フィールドの値は0または1のみとすることができる。 Do メソッドの最初の呼び出しが完了した後に done doneの値は1に変更されます。 doneの値には、4バイトの uint32 型を使用して、その操作がアトミックな操作であることを確認するために atomic.LoadUint32 関数でその値を取得し、それが1であれば関数を実行せずに直接返します。

0の場合、Doメソッドは直ちにフィールドmをロックします。ここでロックが適用されない場合、複数の goroutine がすべて 0 のときに Do メソッドを同時に実行すると、関数が実行されるため Once は同時並行的に安全です。

ロックした後、再び done フィールドを生成し、条件を満たした場合、入力された関数を実行し、アトミック操作関数 atomic.StoreUint32 は、その done の値を 1 に設定します。

以下はOnceのソースコードです。

func (o *Once) Do(f func()) {

 if atomic.LoadUint32(&o.done) == 0 {
  o.doSlow(f)
 }
}

func (o *Once) doSlow(f func()) {
 o.m.Lock()
 defer o.m.Unlock()
 if o.done == 0 {
  defer atomic.StoreUint32(&o.done, 1)
  f()
 }
}



ソースコードは非常にクリーンで、GoFデザインパターンのシングルトン・パターンに非常によく似ています。

今回の記事は、Goの並行プログラミングsync.Onceについてです。Goのsync.Onceについては、過去の記事を検索するか、引き続き以下の記事を閲覧してください。