Go36-32-context.Context

  

context.Context

  

sync.WaitGroup类型是一个实现一对多goroutine协作流程的同步工具。还有另一种工具也可以实现这种协作流程。

  

回顾sync.WaitGroup实现协作流程

  

在使用WaitGroup的时候,建议是用“先统一添加,再并发做,最后等待”的模式来构建协作流程。要避免并发的调用添加方法。这就带来一个问题,需要在一开始就能确定执行子任务的goroutine的数量,至少也是在启动goroutine之前。
下面是一个示例,稍微做了一些改造:

  
 <代码类=" language-go ">包主要
  
  导入(
  “时间”
  “fmt”
  “同步”
  “同步/原子”
  )
  
  func coordinateWithWaitGroup () {
  总:=12
  var num int32
  var wg sync.WaitGroup//定义好goroutine中返回前要执行的推迟函数
  deferFunc:=func () {
  wg.Done ()
  }
  我:=0;我& lt;总;我+ + {
  wg.Add (1)
  addNum(和num, deferFunc)
  }
  wg.Wait ()
  }//这个函数的延迟函数通过参数来给出
  func addNum (numP * int32, id int, deferFunc func ()) {
  推迟deferFunc ()
  我:=1;;我+ + {
  currNum:=atomic.LoadInt32 (numP)
  newNum:=currNum + 1
  time . sleep(时间。毫秒* 200)
  如果原子。CompareAndSwapInt32 (numP currNum newNum) {
  fmt。Printf (" id: % 2 d第% 2 d次更新num成功:% d \ n”, id,我,newNum)
  打破
  }
  }
  }
  
  函数main () {
  coordinateWithWaitGroup ()
  } 
  

这里的改造是为了更像之后要使用上下文包时的用法,不过在使用规则上还是满足WaitGroup的要求的。

  

通过上下文包实现协作流程

  

这里就是要在写一个coordinateWithWaitContext函数,来代替上面的coordinateWithWaitGroup函数。两个函数要具有相同的功能。
这里先直接给出示例代码了:

  
 <代码类=" language-go "> func coordinateWithWaitContext () {
  总:=12
  var num int32
  cxt cancelFunc:=context.WithCancel (context.Background ())//定义好goroutine中返回前要执行的推迟函数,这里用到了上面的cancelFunc
  deferFunc:=func () {
  如果atomic.LoadInt32(和num)==int32 () {
  cancelFunc ()
  }
  }
  我:=0;我& lt;总;我+ + {
  addNum(和num, deferFunc)
  }
  & lt; - cxt.Done ()
  } 
  

所有的变化都在上面这个函数里了。这里先后调用了context.Background函数和context.WithCancel函数。得到了一个可撤销context.Context类型的值,赋值给了变量cxt。还有一个上下文。CancelFunc类型的撤销函数,赋值给了变量CancelFunc。
这里在判断goroutine执行完毕的依据是通过判断num里的值。一旦判断完成,就会调用之前准备好的CancelFunc函数,此时cxt.Done函数返回的通道就会接收到值,结束等待。

  

<强>和WaitGroup的比较
WaitGroup需要事先知道所有goroutine的数量,而这环境里更关心是否满足某个条件,一旦条件满足就可以退出。
这里我想提一下python,让我想到python中了的,循环和而循环。能用的循环就不要用而循环。使用而循环可能由于条件判断复杂了,造成条件永远无法满足而成了死循环。使用的循环的话就没有这个问题了。不过当循环的退出和数量没有关系时,只能用而循环了。
就好比WaitGroup,如果可以通过goroutine的数量判断,那么应该还是使用WaitGroup好。如果遇到结束条件和goroutine数量无关的时候,就只能用上下文了。

  

context.Context类型

  

上下文。上下文类型,是在1.7去发布时才被加入到标准库的。而后,标准库中的很多其他代码包都为了支持它而进行了扩展,包括:os/exec包,净包、数据库/sql包,运行时/pprof包和运行时/跟踪包,等等。
之所以会收到众多代码包的积极支持,主要因为它是一种非常通用的同步工具。它的值不但可以任意的扩散,而且还可以被用来传递额外的信息和信号。就是上下文类型可以提供一个代表上下文的值,之类值是并发安全的,也就是说它可以被传播给多个goroutine。

  

<强>接口类型
上下文最新实际是一个接口类型,在上下文包中实现该接口的所有私有类型,都是基于某个数据类型的指针类型,所以,如此传播并不会影响该类型值的功能和安全。

  

<强>可繁衍的
上下文类型的值是可以繁衍的,这意味着可以通过一个上下文值产生出任意个子值。这些子值可以携带父值的属性和数据,也可以相应通过其父值传达的信号。如此,所有的上下文值共同构成了一颗代表了上下文全貌的属性结构。树的根节点是一个已经在上下文包中预定义好的上下文值,它是全局唯一的。通过调用上下文。背景函数,就可以获取到它。

Go36-32-context.Context