banner
uyoung

uyoung

twitter

golang並發編程

golang

概念#

  • Concurrency is not parallelism: 並發不等於並行
  • channel 有不同種類的類型 <-, -> 箭頭指明了 channel 的方向
  • channel 的 buffer size 表明了 channel 是同步 channel 還是非同步 channel
  • CAS: compare-and-swap: 在設計並發算法時用到了 CAS 技術,提高了多線程執行的安全性

CAS#

CAS 原理: CAS 有三個操作數:內存值 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 { // Returns receive-only channel of strings.
    c := make(chan string)
    go func() { // We launch the goroutine from inside the function.
        for i := 0; ; i++ {
            c <- fmt.Sprintf("%s %d", msg, i)
            time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
        }
    }()
    return c // Return the channel to the caller.
}

複用 channel (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 有如下 4 個函數用來由上級父 context 衍生新的 context。

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 即上下文,如下代碼為構造一個,帶取消函數的 context, 即執行取消方法之後,watch 函數響應退出。

context 通常用來處理 request 請求的每個 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("可以了,通知監控停止")
  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)
    }
  }
}

worker 調度#

基礎的 worker 隊列調度方式

idleWorker<-workerQueue

func(){
  idleWorker.DoTask()
  defer workerQueue<-idleWorker
}

Reference#

golang 並發編程

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。