Documentation
¶
Overview ¶
syncパッケージは相互排他ロックなどの基本的な同期プリミティブを提供します。Once と WaitGroup 以外の型は、低レベルのライブラリルーチンでの使用を意図しています。より高レベルな同期はチャネルと通信を介して行う方が良いです。
このパッケージで定義された型を含む値は、コピーしないでください。
Index ¶
- func OnceFunc(f func()) func()
- func OnceValue[T any](f func() T) func() T
- func OnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2)
- type Cond
- type Locker
- type Map
- func (m *Map) Clear()
- func (m *Map) CompareAndDelete(key, old any) (deleted bool)
- func (m *Map) CompareAndSwap(key, old, new any) (swapped bool)
- func (m *Map) Delete(key any)
- func (m *Map) Load(key any) (value any, ok bool)
- func (m *Map) LoadAndDelete(key any) (value any, loaded bool)
- func (m *Map) LoadOrStore(key, value any) (actual any, loaded bool)
- func (m *Map) Range(f func(key, value any) bool)
- func (m *Map) Store(key, value any)
- func (m *Map) Swap(key, value any) (previous any, loaded bool)
- type Mutex
- type Once
- type Pool
- type RWMutex
- type WaitGroup
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func OnceFunc ¶ added in v1.21.0
func OnceFunc(f func()) func()
OnceFuncは関数fを一度だけ呼び出す関数を返します。返された関数は並行して呼び出すことができます。
fがパニックを起こした場合、返された関数はすべての呼び出しで同じ値でパニックを起こします。
func OnceValue ¶ added in v1.21.0
func OnceValue[T any](f func() T) func() T
OnceValue は、関数 f を一度だけ呼び出し、f の戻り値を返す関数を返します。 返された関数は、同時に呼び出すことができます。
f がパニックを起こした場合、返された関数はすべての呼び出しで同じ値を持つパニックを発生させます。
Example ¶
この例はOnceValueを使って「高コスト」な計算を一度だけ実行します。 同時に使用しても一度だけ実行されます。
package main
import (
"github.com/shogo82148/std/fmt"
"github.com/shogo82148/std/sync"
)
func main() {
once := sync.OnceValue(func() int {
sum := 0
for i := 0; i < 1000; i++ {
sum += i
}
fmt.Println("Computed once:", sum)
return sum
})
done := make(chan bool)
for i := 0; i < 10; i++ {
go func() {
const want = 499500
got := once()
if got != want {
fmt.Println("want", want, "got", got)
}
done <- true
}()
}
for i := 0; i < 10; i++ {
<-done
}
}
Output: Computed once: 499500
func OnceValues ¶ added in v1.21.0
func OnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2)
OnceValuesは、fを一度だけ呼び出し、fによって返された値を返す関数を返します。返された関数は並行して呼び出すことができます。
fがパニックを引き起こした場合、返された関数はすべての呼び出しで同じ値でパニックを起こします。
Example ¶
この例はOnceValuesを使ってファイルを一度だけ読み込みます。
package main
import (
"github.com/shogo82148/std/fmt"
"github.com/shogo82148/std/os"
"github.com/shogo82148/std/sync"
)
func main() {
once := sync.OnceValues(func() ([]byte, error) {
fmt.Println("Reading file once")
return os.ReadFile("example_test.go")
})
done := make(chan bool)
for i := 0; i < 10; i++ {
go func() {
data, err := once()
if err != nil {
fmt.Println("error:", err)
}
_ = data // この例ではデータは無視します
done <- true
}()
}
for i := 0; i < 10; i++ {
<-done
}
}
Output: Reading file once
Types ¶
type Cond ¶
type Cond struct {
// 条件を観察または変更する間は、Lを保持します
L Locker
// contains filtered or unexported fields
}
Condはイベントの発生を待つまたは宣言するための待機ポイントである条件変数を実装します。
各Condには関連付けられたLocker L(通常は *Mutex または *RWMutex)があり、条件を変更するときや Cond.Wait メソッドを呼び出すときに保持する必要があります。
最初の使用後にCondをコピーしてはいけません。
the Go memory model の用語では、Condは Cond.Broadcast または Cond.Signal の呼び出しが、それによってブロック解除される任意のWait呼び出しよりも 「先に同期する」ように配置します。
単純な使用例では、ユーザーはチャネルを使用する方がCondよりも優れています(Broadcastはチャネルを閉じることに対応し、Signalはチャネルに送信することに対応します)。
sync.Cond の代わりに他のものについては、Roberto Clapis's series on advanced concurrency patterns と Bryan Mills's talk on concurrency patterns を参照してください。
func (*Cond) Broadcast ¶
func (c *Cond) Broadcast()
Broadcastは、cで待機しているすべてのゴルーチンを起こします。
呼び出し元がc.Lを保持していることは許可されていますが、必須ではありません。
func (*Cond) Signal ¶
func (c *Cond) Signal()
cに待機しているゴルーチンがあれば、Signalは1つのゴルーチンを起こします。
呼び出し元がc.Lを保持していることは必須ではありませんが、許可されています。
Signal()はゴルーチンのスケジューリングの優先順位に影響を与えません。他のゴルーチンがc.Lをロックしようとしている場合、"待機中"のゴルーチンよりも先に起きる場合があります。
func (*Cond) Wait ¶
func (c *Cond) Wait()
Waitはc.Lのロックを解除して、呼び出し元のゴルーチンの実行を一時停止します。 後で再開すると、Waitは戻る前にc.Lをロックします。他のシステムとは異なり、 Waitは Cond.Broadcast または Cond.Signal によって起こされない限り戻りません。
Waitが待機している間、c.Lはロックされていないため、呼び出し元は 待機が返るときに条件が真であることを前提とすることはできません。代わりに、 呼び出し元はループ内でWaitを使用する必要があります:
c.L.Lock()
for !condition() {
c.Wait()
}
... 条件を活用する ...
c.L.Unlock()
type Map ¶ added in v1.9.0
type Map struct {
// contains filtered or unexported fields
}
MapはGoのmap[any]anyと似ていますが、追加のロックや調整なしで 複数のゴルーチンから同時に使用しても安全です。 ロード、ストア、削除は平均定数時間で実行されます。
Map型は特殊化されています。ほとんどのコードでは代わりに単純なGoのmapを使用すべきであり、 型の安全性を向上させ、他の不変条件とマップのコンテンツを維持しやすくするために別個のロックや調整を行うべきです。
Map型は2つの一般的な使用例に最適化されています:(1)特定のキーのエントリが一度しか書き込まれずに多くの回数読み取られる場合、 つまり成長のみのキャッシュ、または(2)複数のゴルーチンが交差しないキーのセットの読み取り、書き込み、上書きを行う場合。 これらの2つの場合において、Mapの使用は、別個の Mutex や RWMutex とペアになったGoのマップと比較して、 ロックの競合を大幅に減らすことができます。
ゼロ値のMapは空で使用準備ができています。Mapは最初の使用後にコピーされてはなりません。
the Go memory model の用語では、Mapは書き込み操作が行われたときにそれに続く読み取り操作を "書き込みより前に同期します"。 ここで、読み取りと書き込み操作は以下のように定義されます。 Map.Load、Map.LoadAndDelete、Map.LoadOrStore、Map.Swap、Map.CompareAndSwap、Map.CompareAndDelete は読み取り操作です。 Map.Delete、Map.LoadAndDelete、Map.Store、Map.Swap は書き込み操作です。 Map.LoadOrStore は、loadedがfalseで返された場合に書き込み操作です。 Map.CompareAndSwap は、swappedがtrueで返された場合に書き込み操作です。 Map.CompareAndDelete は、deletedがtrueで返された場合に書き込み操作です。
func (*Map) CompareAndDelete ¶ added in v1.20.0
CompareAndDeleteは、keyの値がoldと等しい場合、そのエントリを削除します。 oldの値は比較可能な型である必要があります。
マップにkeyの現在の値がない場合、CompareAndDeleteはfalseを返します(oldの値がnilインターフェース値であっても)。
func (*Map) CompareAndSwap ¶ added in v1.20.0
CompareAndSwapは、キーの古い値と新しい値を交換します。 マップに格納されている値が古い値と等しい場合にのみ行われます。 古い値は比較可能な型でなければなりません。
func (*Map) Load ¶ added in v1.9.0
Loadは、キーに対応するマップ内の値を返します。もし値が存在しない場合はnilを返します。 okの結果は、値がマップ内で見つかったかどうかを示します。
func (*Map) LoadAndDelete ¶ added in v1.15.0
LoadAndDelete はキーに対応する値を削除し、もし値が存在する場合は以前の値を返します。 読み込まれた結果はキーが存在するかどうかを報告します。
func (*Map) LoadOrStore ¶ added in v1.9.0
LoadOrStore は、キーが存在する場合は既存の値を返します。 それ以外の場合は、指定された値を格納して返します。 読み込まれた結果が true の場合、値は読み込まれ、false の場合は格納されました。
func (*Map) Range ¶ added in v1.9.0
Rangeはマップ内の各キーと値に対して順番にfを呼び出します。 もしfがfalseを返す場合、Rangeは繰り返しを停止します。
RangeはMapの内容に一貫したスナップショットに必ずしも対応していません: 各キーは複数回訪れませんが、任意のキーの値が同時に格納または削除される場合(fによっても含まれます)、 RangeはRange呼び出し中の任意の時点からのそのキーのマッピングを反映することがあります。 Rangeはレシーバー上の他のメソッドをブロックしません。f自体もmの任意のメソッドを呼び出すことができます。
fが一定回数の呼び出し後にfalseを返す場合でも、Rangeはマップ内の要素数の数に比例するO(N)の可能性があります。
type Mutex ¶
type Mutex struct {
// contains filtered or unexported fields
}
Mutexは相互排他ロックです。 Mutexのゼロ値はロックされていないMutexです。
Mutexは最初の使用後にコピーしてはいけません。
the Go memory model の用語では、Mutex.Unlock のn回目の呼び出しはm回目の Mutex.Lock の前に同期されます(n < m)。 Mutex.TryLock の成功した呼び出しはLockの呼び出しと同等です。 TryLockの失敗した呼び出しはどのような「同期前の」関係も確立しません。
func (*Mutex) Lock ¶
func (m *Mutex) Lock()
Lock は m をロックします。 もし既にロックが使用中である場合、呼び出し元のゴルーチンは ミューテックスが利用可能になるまでブロックされます。
type Once ¶
type Once struct {
// contains filtered or unexported fields
}
Onceは正確に1つのアクションを実行するオブジェクトです。
Onceは最初の使用後にコピーしてはいけません。
the Go memory model の用語では、fからの戻り値はonce.Do(f)の呼び出しの戻り値よりも前に同期します。
Example ¶
package main
import (
"github.com/shogo82148/std/fmt"
"github.com/shogo82148/std/sync"
)
func main() {
var once sync.Once
onceBody := func() {
fmt.Println("Only once")
}
done := make(chan bool)
for i := 0; i < 10; i++ {
go func() {
once.Do(onceBody)
done <- true
}()
}
for i := 0; i < 10; i++ {
<-done
}
}
Output: Only once
func (*Once) Do ¶
func (o *Once) Do(f func())
Doは、Once のインスタンスで最初にDoが呼び出された場合のみ、関数fを呼び出します。つまり、次のように与えられた場合、
var once Once
もしこのように複数回once.Do(f)が呼び出された場合、最初の呼び出しのみがfを実行し、 それぞれの呼び出しでfが異なる値を持っていても、その値に関係なく実行されます。各関数の実行には新しいOnceのインスタンスが必要です。
Doは、一度だけ実行する必要のある初期化に使用されます。fは引数なしの関数ですので、Doによって呼び出される関数の引数を捕捉するために関数リテラルを使用する必要があるかもしれません:
config.once.Do(func() { config.init(filename) })
Doへの呼び出しがfの返り値が返されるまで戻らないため、fがDoの呼び出しを引き起こすとデッドロックが発生します。
fがパニックした場合、Doはそれを戻ったとみなします。その後のDoの呼び出しはfを呼び出さずに返ります。
type Pool ¶ added in v1.3.0
type Pool struct {
// Newは、Getがnilを返す場合に値を生成するための関数を指定するオプションです。
// Getの呼び出しと同時に変更することはできません。
New func() any
// contains filtered or unexported fields
}
プールとは個別に保存および取り出しが可能な一時オブジェクトのセットです。
プールに保存されたアイテムは、通知なしにいつでも自動的に削除される可能性があります。 これが発生する際にプールが唯一の参照を保持している場合、そのアイテムは解放される可能性があります。
プールは、複数のゴルーチンによる同時の使用に対して安全です。
プールの目的は、割り当てされたが未使用のアイテムをキャッシュして後で再利用することで、 ガベージコレクタにかかる負荷を軽減することです。つまり、効率的かつスレッドセーフな無料リストを構築することを簡単にします。 ただし、プールはすべての無料リストに適しているわけではありません。
プールの適切な使用例は、パッケージの独立した並行クライアント間で静に共有され、 潜在的に再利用される一時アイテムのグループを管理することです。 プールは、多くのクライアント間で割り当てのオーバーヘッドを分散する方法を提供します。
プールの良い使用例は、fmtパッケージにあります。これは一時的な出力バッファのサイズを動的に管理しています。 このストアは、負荷がかかった場合(多くのゴルーチンがアクティブに印刷している場合)に拡大し、静かな場合には縮小します。
一方、短命なオブジェクトの一部として維持される無料リストは、プールには適していません。 なぜなら、このシナリオではオーバーヘッドを適切に分散させることができないからです。 このようなオブジェクトは、独自の無料リストを実装する方が効率的です。
プールは、最初の使用後にコピーしないでください。
the Go memory model の用語では、Put(x)の呼び出しは、 同じ値xを返す Pool.Get の呼び出しよりも「先に同期します」。 同様に、Newがxを返す呼び出しは、 同じ値xを返すGetの呼び出しよりも「先に同期します」。
Example ¶
Log(os.Stdout, "path", "/search?q=flowers")
Output: 2006-01-02T15:04:05Z path=/search?q=flowers
type RWMutex ¶
type RWMutex struct {
// contains filtered or unexported fields
}
RWMutexは、読み込み/書き込み相互排他的なロックです。 ロックは任意の数の読み込み者または単一の書き込み者によって保持することができます。 RWMutexのゼロ値はロックされていないミューテックスです。
RWMutexは、最初の使用後にコピーしてはいけません。
他のゴルーチンがすでに1つ以上のリーダーとしてロックを保持している状態で RWMutex.Lock を呼び出すと、 同時に行われる RWMutex.RLock の呼び出しは、ライターがロックを取得して解放するまでブロックされます。 これは、ロックが最終的にライターに利用可能になることを保証するためです。 この動作により、再帰的な読み取りロックは禁止されます。 RWMutex.RLock を RWMutex.Lock にアップグレードすることはできず、 RWMutex.Lock を RWMutex.RLock にダウングレードすることもできません。
the Go memory model の用語では、 n番目の RWMutex.Unlock への呼び出しは、任意のn < mに対するm番目のLockへの呼び出しよりも 「先に同期します」。これは Mutex と同様です。 RLockへの任意の呼び出しに対して、nが存在し、 n番目のUnlockへの呼び出しはそのRLockへの呼び出しよりも「先に同期します」、 そして対応する RWMutex.RUnlock への呼び出しも同様に「先に同期します」。
func (*RWMutex) Lock ¶
func (rw *RWMutex) Lock()
Lockはrwを書き込み用にロックします。 もし既に読み込みや書き込みのためにロックされている場合、 Lockは利用可能になるまでブロックします。
func (*RWMutex) RLock ¶
func (rw *RWMutex) RLock()
RLockはrwの読み取りのためにロックします。
再帰的な読み取りのために使用すべきではありません。ブロックされたLock呼び出しは、 新しい読み取り者がロックを取得することを排除します。RWMutex 型のドキュメントを参照してください。
func (*RWMutex) RLocker ¶
RLockerは Locker インターフェースを返します。このインターフェースは、rw.RLockとrw.RUnlockを呼び出して [Locker.Lock] と [Locker.Unlock] メソッドを実装します。
func (*RWMutex) RUnlock ¶
func (rw *RWMutex) RUnlock()
RUnlockは1回の RWMutex.RLock 呼び出しを元に戻します。 他の同時読み取りプロセスには影響しません。 RUnlockが呼び出される時にrwが読み取りロックされていない場合、ランタイムエラーが発生します。
func (*RWMutex) TryLock ¶ added in v1.18.0
TryLockは、rwを書き込み用にロックしようとし、成功したかどうかを報告します。
TryLockの正しい使用法は存在しますが、それらはまれであり、 mutexの特定の使用法におけるより深刻な問題の兆候であることが多いため、 TryLockの使用は避けるべきです。
func (*RWMutex) TryRLock ¶ added in v1.18.0
TryRLockはrwを読み取りロックしようとし、成功したかどうかを報告します。
TryRLockの正しい使用方法は存在しますが、それらは稀であり、 TryRLockの使用はしばしばミューテックスの特定の使用法におけるより深刻な問題の兆候です。
func (*RWMutex) Unlock ¶
func (rw *RWMutex) Unlock()
Unlockは書き込みのためにrwをアンロックします。Unlockに入る前にrwが書き込み用にロックされていない場合、ランタイムエラーとなります。
Mutexと同様に、ロックされた RWMutex は特定のゴルーチンに関連付けられていません。あるゴルーチンがRWMutexを RWMutex.RLock(RWMutex.Lock)し、別のゴルーチンが RWMutex.RUnlock(RWMutex.Unlock)するようにすることができます。
type WaitGroup ¶
type WaitGroup struct {
// contains filtered or unexported fields
}
WaitGroupは、主に複数のゴルーチンやタスクの終了を待つために使われるカウント型セマフォです。
通常、メインゴルーチンは各タスクを新しいゴルーチンで起動する際に WaitGroup.Go を呼び出し、 すべてのタスクが完了するまで WaitGroup.Wait を呼び出して待機します。例:
var wg sync.WaitGroup wg.Go(task1) wg.Go(task2) wg.Wait()
WaitGroupは、Goを使わずにタスクの追跡にも利用でき、WaitGroup.Add と WaitGroup.Done を使います。
前述の例は、AddとDoneを使って明示的にゴルーチンを作成する形でも書き換えられます:
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
task1()
}()
wg.Add(1)
go func() {
defer wg.Done()
task2()
}()
wg.Wait()
このパターンは WaitGroup.Go より前のコードでよく使われます。
WaitGroupは最初に使用した後でコピーしてはいけません。
Example ¶
この例では、複数のURLを同時にフェッチし、WaitGroupを使用して、すべてのフェッチが完了するまでブロックします。
package main
import (
"github.com/shogo82148/std/net/http"
"github.com/shogo82148/std/sync"
)
func main() {
var wg sync.WaitGroup
var urls = []string{
"http://www.golang.org/",
"http://www.google.com/",
"http://www.example.com/",
}
for _, url := range urls {
// URLを取得するためにゴルーチンを起動します。
wg.Go(func() {
// URLを取得します。
http.Get(url)
})
}
// すべてのHTTPフェッチが完了するまで待ちます。
wg.Wait()
}
Example (AddAndDone) ¶
この例はメインの例と同等ですが、Goの代わりにAdd/Doneを使用します。
package main
import (
"github.com/shogo82148/std/net/http"
"github.com/shogo82148/std/sync"
)
func main() {
var wg sync.WaitGroup
var urls = []string{
"http://www.golang.org/",
"http://www.google.com/",
"http://www.example.com/",
}
for _, url := range urls {
// WaitGroupのカウンターをインクリメントします。
wg.Add(1)
// URLを取得するために、ゴルーチンを起動します。
go func(url string) {
// ゴルーチンが完了したら、カウンタを減らす。
defer wg.Done()
// URLを取得する。
http.Get(url)
}(url)
}
// すべてのHTTPフェッチが完了するまで待ちます。
wg.Wait()
}
func (*WaitGroup) Add ¶
Addは、WaitGroup のタスクカウンターにdelta(負でも可)を加算します。 カウンターがゼロになると、WaitGroup.Wait でブロックされているすべてのゴルーチンが解放されます。 カウンターが負になると、Addはパニックを起こします。
呼び出し側は [WaitGroup.Go]を優先して使用すべきです。
カウンターがゼロのときに正のdeltaで呼び出す場合、Waitの前に実行する必要があります。 カウンターがゼロより大きいときに正のdeltaで呼び出す場合や、負のdeltaで呼び出す場合は、いつでも実行できます。 通常、Addの呼び出しは、ゴルーチンや待機対象のイベントを作成する文の前に実行します。 WaitGroupを複数の独立したイベントセットの待機に再利用する場合は、 新しいAddの呼び出しは、すべての前回のWait呼び出しが返った後に実行する必要があります。 WaitGroupの例を参照してください。
func (*WaitGroup) Done ¶
func (wg *WaitGroup) Done()
Doneは、WaitGroup のタスクカウンターを1減算します。 Add(-1)と同じです。
呼び出し側は WaitGroup.Go を優先して使用すべきです。
the Go memory model の用語では、Doneの呼び出しは、 それによって解除されるWait呼び出しの返り値より「先に同期」します。
func (*WaitGroup) Go ¶ added in v1.25.0
func (wg *WaitGroup) Go(f func())
Goはfを新しいゴルーチンで呼び出し、そのタスクを WaitGroup に追加します。 fが返ると、そのタスクはWaitGroupから削除されます。
fはパニックを起こしてはいけません。
WaitGroupが空の場合、Goは WaitGroup.Wait より前に実行されなければなりません。 通常は、Goでタスクを開始してからWaitを呼び出します。 WaitGroupが空でない場合、Goはいつでも実行できます。 Goで開始されたゴルーチン自身がGoを呼び出すこともできます。 WaitGroupを複数の独立したタスクセットの待機に再利用する場合は、 新しいGoの呼び出しは、すべての前回のWait呼び出しが返った後に実行する必要があります。
the Go memory model の用語では、fの返り値は、 それによって解除されるWait呼び出しの返り値より「先に同期」します。