Goのcontextによるキャンセルやタイムアウト - oinume journal context.WithCancel, WithTimeout で知っておいた方が良いこと - Carpe Diem
package main
import (
"context"
"fmt"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
wg.Add(1)
go func() {
defer wg.Done()
if err := printHello(ctx); err != nil {
fmt.Printf("cannot print greeting: %v\n", err)
return
}
}()
wg.Add(1)
go func() {
defer wg.Done()
if err := printGoodbye(ctx); err != nil {
fmt.Printf("cannot print goodbye: %v\n", err)
return
}
}()
wg.Wait()
}
func printHello(ctx context.Context) error {
ctx, cancel := context.WithTimeout(ctx, 4*time.Second)
defer cancel()
switch _, err := communicate(ctx); {
case err != nil:
return err
}
fmt.Printf("%s world!\n", "hello")
return nil
}
func printGoodbye(ctx context.Context) error {
ctx, cancel := context.WithTimeout(ctx, 2*time.Second)
defer cancel()
switch _, err := communicate(ctx); {
case err != nil:
return err
}
fmt.Printf("%s world!\n", "goodbye")
return nil
}
func communicate(ctx context.Context) (string, error) {
// n秒後に処理を継続
select {
case <-ctx.Done():
return "", ctx.Err()
case <-time.After(5 * time.Second):
//fmt.Printf("1 second elapsed\n")
}
return "handshake", nil
}
communicate
を5秒(タイムアウト値より長い)にする → タイムアウトしてcannot print greeting: deadline exceeded
communicate
を3秒(helloのタイムアウト値より短い、goodbyeのタイムアウト値より長い)にする → helloは完了するがgoodbyeはタイムアウトするcommunicate
を1秒(タイムアウト値より短い)にする → 両方完了する
親のcontextをキャンセルすると、子のcontextもキャンセルされる
package main
import (
"context"
"fmt"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
wg.Add(1)
go func() {
defer wg.Done()
if err := printHello(ctx); err != nil {
fmt.Printf("cannot print greeting: %v\n", err)
cancel()
return
}
}()
wg.Add(1)
go func() {
defer wg.Done()
if err := printGoodbye(ctx); err != nil {
fmt.Printf("cannot print goodbye: %v\n", err)
return
}
}()
wg.Wait()
}
func printHello(ctx context.Context) error {
ctx, cancel := context.WithTimeout(ctx, 2*time.Second)
defer cancel()
switch _, err := communicate(ctx); {
case err != nil:
return err
}
fmt.Printf("%s world!\n", "hello")
return nil
}
func printGoodbye(ctx context.Context) error {
ctx, cancel := context.WithTimeout(ctx, 4*time.Second)
defer cancel()
switch _, err := communicate(ctx); {
case err != nil:
return err
}
fmt.Printf("%s world!\n", "goodbye")
return nil
}
func communicate(ctx context.Context) (string, error) {
// n秒後に処理を継続
select {
case <-ctx.Done():
return "", ctx.Err()
case <-time.After(5 * time.Second):
//fmt.Printf("1 second elapsed\n")
}
return "handshake", nil
}
⇒ printHelloがタイムアウト → 親の cancel()
を実行 → 子の ctx.Done()
が実行されてprintGoodbyeも終了する
cannot print greeting: context deadline exceeded
cannot print goodbye: context canceled