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 并发编程

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。