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")
}

全局變量

簡單,可是傳遞的數據只能一寫多讀。spa

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)。channel 在 Golang 中是核心類型。code

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)
    stop<-true
    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() 方法能夠拿到上下文的根節點。經常使用方法有:string

  • 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 調用會沿着上下文樹的邊向下通知到每個子節點。it

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 來避免睡眠一段時間:io

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"))
}
相關文章
相關標籤/搜索