概念#
- Concurrency is not parallelism: 並行は並列とは異なる
- channel には異なる種類の型があり、<-、-> の矢印が channel の方向を示す
- channel のバッファサイズは、同期チャネルか非同期チャネルかを示す
- CAS: compare-and-swap(比較と交換):並行アルゴリズムの設計に CAS 技術を使用し、マルチスレッドの安全性を向上させる
CAS#
CAS の原理:CAS には 3 つのオペランドがあります:メモリの値 V、古い予想値 A、変更する値 B。予想値 A とメモリの値 V が等しい場合にのみ、メモリの値を B に変更して true を返し、それ以外の場合は何もせずに false を返します。
- CAS 操作は、オペレーティングシステムが対応するインターフェースを提供し、ハードウェアレベルでサポートされています。
if *addr == old {
*addr = new
return true
}
return false
- CAS による golang のロックの実装
package main
import (
"sync/atomic"
)
type Mutex struct {
state int32
}
func (m *Mutex) Lock() {
for(!atomic.CompareAndSwapInt32(&m.state, 0, 1)) {
return
}
}
func (m *Mutex) Unlock() {
atomic.CompareAndSwapInt32(&m.state, 1, 0)
}
- CAS を使用した sync.Map の実装:goroutine のスレッドセーフ
初級#
ジェネレータ (generator)#
channel は golang で基本的な変数型(int、string など)と同じく、最上位の型です。ジェネレータは、関数が channel を返し、関数内で channel に要素を追加するものです。
ジェネレータパターンは非常に一般的であり、最も単純なプロデューサー・コンシューマーパターンやパブリッシャー・サブスクライバーパターンを実装するために使用できます。
func boring(msg string) <-chan string { // 受信専用の文字列型チャネルを返す
c := make(chan string)
go func() { // ゴルーチンを関数内から起動する
for i := 0; ; i++ {
c <- fmt.Sprintf("%s %d", msg, i)
time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
}
}()
return c // チャネルを呼び出し元に返す
}
チャネルの再利用(select)#
func main() {
c := boring("Joe")
timeout := time.After(5 * time.Second)
timer := time.NewTimer(3 * time.Second)
for {
select {
case s := <-c:
fmt.Println(s)
case <-timer.C:
// タイマーイベント
timer.Reset(3 * time.Second)
case <-timeout: // タイムアウトイベントの監視
fmt.Println("You talk too much.")
return
case <-quit: // 終了イベントの監視
return
}
}
}
並行制御(WaitGroup->chan->context)#
WaitGroup#
WaitGroup は、並行制御の基礎となるクラスです。
func main() {
var wg sync.WaitGroup
wg.Add(2)
go func() {
time.Sleep(2*time.Second)
fmt.Println("1号完成")
wg.Done()
}()
go func() {
time.Sleep(2*time.Second)
fmt.Println("2号完成")
wg.Done()
}()
wg.Wait()
fmt.Println("よし、みんな終わった、仕事終了")
}
Context#
context は、上位の親 context から新しい context を派生させるために使用される 4 つの関数があります。
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
func WithValue(parent Context, key, val interface{}) Context
- WithCancel:ctx とキャンセル関数を返します。
- WithDeadline:ctx とキャンセル関数を返します。時間が deadline を超えると自動的にキャンセルされます。
- WithTimeout:WithDeadline と同様ですが、タイムアウト時間です。
- WithValue:メタデータを渡し、ctx.Value (key) の形式でデータを取得します。
context は、通常、リクエストの各 goroutine を処理するために使用されます。
func main() {
ctx, cancel := context.WithCancel(context.Background())
go watch(ctx,"【監視1】")
go watch(ctx,"【監視2】")
go watch(ctx,"【監視3】")
time.Sleep(10 * time.Second)
fmt.Println("OK、監視を停止します")
cancel()
// 監視が停止したかどうかを確認するため、監視の出力がない場合は停止したとみなします
time.Sleep(5 * time.Second)
}
func watch(ctx context.Context, name string) {
for {
select {
case <-ctx.Done():
fmt.Println(name,"監視終了、停止しました...")
return
default:
fmt.Println(name,"goroutine監視中...")
time.Sleep(2 * time.Second)
}
}
}
ワーカースケジューリング#
基本的なワーカーキュースケジューリングの方法です。
idleWorker<-workerQueue
func(){
idleWorker.DoTask()
defer workerQueue<-idleWorker
}