Golang同時goroutine間通信


Goroutine間でよく使われる通信方式は次のとおりです.
  • グローバル変数
  • channel
  • contextコンテキスト
  • sync.WaitGroup


    すべてのタスクの完了を単純に待つ場合は、sync.WaitGroupを使用します.
    package main
    
    import (
       "fmt"
       "sync"
       "time"
    )
    
    func main() {
       var wg sync.WaitGroup
       for i := 0; i < 3; i++ {
           wg.Add(1)
           go func() {
               fmt.Println("func run")
               time.Sleep(time.Second)
               wg.Done()
           }()
       }
       wg.Wait()
       fmt.Println("main done")
    }
    

    グローバル変数


    簡単ですが、伝達されたデータは一書多読しかできません.
    package main
    
    import (
        "fmt"
        "time"
    )
    
    var stop bool
    
    func main() {
        go f()
        time.Sleep(2 * time.Second)
        stop = false
        time.Sleep(2 * time.Second)
        fmt.Println("main done")
    }
    
    func f() {
        for stop {
            fmt.Println("still run")
            time.Sleep(time.Second)
        }
    }
    

    channel


    CSP同時プログラミングモデル(Communicating Sequential Process).チャンネルはGolangでコアタイプです.
    Golangのselectメカニズムは言語面でLinuxのようなselect機能を実現し、複数のファイル記述子の読み書きイベントを傍受し、イベント発生時にアプリケーション処理をアクティブに通知することができる.Golangのselectはdefaultを設定し、リスニングされたイベントがすべてブロックされたときに実行することもできます.
    package main
    
    import (
        "fmt"
        "time"
    )
    
    func main() {
        stop := make(chan bool)
        go f(stop)
        time.Sleep(2 * time.Second)
        stoptrue
        time.Sleep(2 * time.Second)
        fmt.Println("main done")
    }
    
    func f(stop chan bool) {
        for {
            select {
                case stop:
                    fmt.Println("done")
                    return
                default:
                    fmt.Println("still run")
                    time.Sleep(time.Second)
            }
        }
    }
    

    contextコンテキスト


    Golangのコンテキストは木構造であり,context.Background()法によりコンテキストのルートノードを得ることができる.一般的な方法は次のとおりです.
  • 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


    サブコンテキストはDoneメソッド(channelを返し、selectで読み取ることができる)を呼び出して、親ノードがcancelを呼び出すかどうかを検出します.上位ノードのcancel呼び出しは、コンテキストツリーのエッジに沿って各サブノードに通知されます.
    package main
    
    import (
        "fmt"
        "time"
        "context"
    )
    
    func main() {
        ctx, myCancel := context.WithCancel(context.Background())
        go func() {
            for {
                select {
                    case ctx.Done():
                        fmt.Println("ctx Done")
                        return
                    default:
                        fmt.Println("goroutine continue")
                        time.Sleep(time.Second)
                }
            }
        }()
        time.Sleep(time.Second * 2)
        myCancel()
        time.Sleep(time.Second)
        fmt.Println("main done")
    }
    

    もちろん、ここではsync.WaitGroupを使って睡眠を避けることができます.
    package main
    
    import (
        "fmt"
        "time"
        "context"
        "sync"
    )
    
    func main() {
        var wg sync.WaitGroup
        wg.Add(1)
        ctx, myCancel := context.WithCancel(context.Background())
        go func() {
            for {
                select {
                    case ctx.Done():
                        fmt.Println("ctx Done")
                        wg.Done()
                        return
                    default:
                        fmt.Println("goroutine continue")
                        time.Sleep(time.Second)
                }
            }
        }()
        time.Sleep(time.Second * 2)
        myCancel()
        wg.Wait()
        fmt.Println("main done")
    }
    

    WithValue

    package main
    
    import (
        "fmt"
        "context"
    )
    
    type favContextKey string
    
    func main() {
        ctx := context.WithValue(context.Background(), favContextKey("hello"), "Go")
        f := func(ctx context.Context, k favContextKey) {
            if v := ctx.Value(k); v != nil {
                fmt.Println("found value:", v)
                return
            }
            fmt.Println("value not found:", k)
        }
        f(ctx, favContextKey("hello"))
        f(ctx, favContextKey("color"))
    }