More Related Content Similar to 明日から使える気になるGo言語による並行処理 Similar to 明日から使える気になるGo言語による並行処理 (20) More from Yuto Matsukubo (10) 明日から使える気になるGo言語による並行処理9. さきほどのコード
func main() {
// go キーワードを関数前に置くだけで
// 並行実行される
go func() {
fmt.Println(“ʕ◔ϖ◔ʔ”)
}()
}
このコードはgoroutineの完了を待っていないので
出力される前に終了してしまう可能性がある!
12. WaitGroup
func main() {
var wg sync.WaitGroup
wg.Add(1) // カウンタを1追加
go func() {
defer wg.Done() // カウンタを1減らす
fmt.Println(“ʕ◔ϖ◔ʔ”)
}()
wg.Wait() // 0になるまでブロック
}
14. Mutex/RWMutex
var m sync.RWMutex
c := 0
increment := func() {
m.Lock() // 書き込みロック
defer m.Unlock()
c++
}
decrement := func() {
m.Lock() // 書き込みロック
defer m.Unlock()
c--
}
read := func() int {
m.RLock() // 読み込みロック
defer m.RUnlock()
return c
}
17. Cond
c := sync.NewCond(new(sync.Mutex))
for i := 0; i < 10; i++ {
go func(i int) {
c.L.Lock()
defer c.L.Unlock()
c.Wait() // goroutineを待機させておく
}(i)
}
for i := 0; i < 10; i++ {
time.Sleep(1 * time.Second)
c.Signal() // 1つずつWaitを解除(FIFO)
}
c.Broadcast() // まとめて解除
18. Cond
c := sync.NewCond(new(sync.Mutex))
for i := 0; i < 10; i++ {
go func(i int) {
c.L.Lock()
defer c.L.Unlock()
c.Wait() // goroutineを待機させておく
}(i)
}
for i := 0; i < 10; i++ {
time.Sleep(1 * time.Second)
c.Signal() // 1つずつWaitを解除(FIFO)
}
c.Broadcast() // まとめて解除
Waitのループに入る時にUnlockが呼ばれるため
事前にLockしておく必要がある
19. Cond
c := sync.NewCond(new(sync.Mutex))
for i := 0; i < 10; i++ {
go func(i int) {
c.L.Lock()
defer c.L.Unlock()
c.Wait() // goroutineを待機させておく
}(i)
}
for i := 0; i < 10; i++ {
time.Sleep(1 * time.Second)
c.Signal() // 1つずつWaitを解除(FIFO)
}
c.Broadcast() // まとめて解除
Waitを抜ける時にLockが掛かるため
deferでUnlockする
21. Once
once := new(sync.Once)
wg := new(sync.WaitGroup)
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
once.Do(func() {
fmt.Println("Once") // 一回だけ呼ばれる
})
wg.Done()
}()
}
wg.Wait()
// 関数が変わっても呼ばれることはない
once.Do(func() {
fmt.Println(“Second")
})
23. Pool
pool := &sync.Pool{
New: func() interface{} {
fmt.Println("Create new instance")
return struct{}{}
},
}
ins := pool.Get() // Create new instance
pool.Put(ins) // プールに戻す
ins = pool.Get() // 使い回されるので初期化されない
27. channelの生成
// interface{}型のチャネルを生成
var ch chan interface{}
ch = make(chan interface{})
// 送信専用チャネル
var ch chan<- interface{}
ch = make(chan<- interface{})
// 受信専用チャネル
var ch <-chan interface{}
ch = make(<-chan interface{})
// キャパシティ付チャネル
// 5つ目を書き込もうとすると読み出されるまでブロックする
var ch <-chan interface{}
ch = make(<-chan interface{}, 4)
28. channelの使用例
ch := make(chan string)
go func() {
time.Sleep(3 * time.Second)
ch <- “(´・ω・`)” // chにメッセージを送信
}()
fmt.Println(<-ch) // メッセージを受信するまでブロック
30. channelとclose/range
c := make(chan int)
go func() {
defer close(c) // 関数を抜けたらclose
for i := 0; i < 3; i++ {
time.Sleep(1 * time.Second)
c <- i
}
}()
for i := range c { // closeされたらループを抜ける
fmt.Println(i)
}
31. channelとselect
c1 := make(chan int)
c2 := make(chan int)
go func() {
defer close(c2) // c2だけcloseする
}()
select { // いずれかのchannelが完了するまでブロックする
case <-c1:
fmt.Println("c1")
case <-c2:
fmt.Println(“c2") // こちらだけが呼ばれる
}
32. channelとselectとTimeout
c1 := make(chan int)
select {
case <-c1:
fmt.Println(“c1")
case <-time.After(1 * time.Second):
// time.Afterは <-chan Time を返す
// c1は終了しないのでこちらが呼ばれる
fmt.Println("timed out”)
}
33. channelとselectとfor
c1 := make(chan int)
go func() {
defer close(c1)
time.Sleep(1 * time.Second)
}()
loop:
for { // 無限ループ
select {
case <-c1:
break loop
default:
// チャネルが完了しない間行われる処理
}
}
36. contextが持つ関数(一部)
func Background() Context
func Todo() Context
func WithCancel(parent Context) (ctx Context, cancel
CancelFunc)
func WithDeadline(parent Context, d time.Time) (Context,
CancelFunc)
func WithTimeout(parent Context, timeout time.Duration)
(Context, CancelFunc)
func WithValue(parent Context, key, val interface{}) Context
37. context.WithCancel
// Contextを生成
ctx, cancel := context.WithCancel(context.Background())
f := func(ctx context.Context) (string, error) {
select {
case <-ctx.Done(): // cancelされた場合Doneが呼ばれる
return "", ctx.Err()
case <-time.After(3 * time.Second):
}
return "(´・ω・`)", nil
}
cancel() // 処理をキャンセルする
_, err := f(ctx)
if err != nil {
fmt.Println(err) // context canceled
}
38. context.WithDeadline
ctx, cancel := context.WithDeadline(
context.Background(),
time.Now().Add(1*time.Second) // deadlineを設定
)
defer cancel()
f := func(ctx context.Context) (string, error) {
time.Sleep(3 * time.Second)
if deadline, ok := ctx.Deadline(); ok {
if deadline.Sub(time.Now()) <= 0 { // deadlineの判定
return "", context.DeadlineExceeded
}
}
return "(´・ω・`)", nil
}
_, err := f(ctx)
if err != nil {
fmt.Println(err) // context deadline exceeded
}
39. context.WithTimeout
ctx, cancel := context.WithTimeout(
context.Background(), 1*time.Second) // タイムアウトを1秒に
defer cancel()
f := func(ctx context.Context) (string, error) {
select {
case <-ctx.Done():
return "", ctx.Err() // 3秒より短いのでタイムアウトする
case <-time.After(3 * time.Second):
return "(´・ω・`)", nil
}
}
_, err := f(ctx)
if err != nil {
fmt.Println(err) // context deadline exceeded
}
40. context.WithValue
type key int
const (
keyPera key = iota
keyNikov key = iota
)
func main() {
// Contextにkey-valueで値をもたせられる
// キーが衝突しないように独自のキー型を定義し区別することを推奨
ctx := context.WithValue(context.Background(), keyPera, "pera")
ctx = context.WithValue(ctx, keyNikov, "nikov")
fmt.Println(ctx.Value(keyPera)) // pera
fmt.Println(ctx.Value(keyNikov)) // nikov
}