1.7 版本时候增加此功能。提供优雅处理goroutine的协同方式
如何通知一个goroutine 需要退出了?
全局变量
var notity = falsevar wg sync.WaitGroupfunc con() {defer wg.Done()for {fmt.Println("执行任务")time.Sleep(time.Millisecond * 300)if notity {break}}}func main() {wg.Add(1)go con()time.Sleep(time.Second * 2)notity = truewg.Wait()}
利用channel
var wg sync.WaitGroupvar notityChan = make(chan bool, 1)func con() {defer wg.Done()LOOP:for {fmt.Println("执行任务")time.Sleep(time.Millisecond * 500)select {case <-notityChan:break LOOPdefault:}}}func main() {wg.Add(1)go con()time.Sleep(time.Second * 5)notityChan <- truewg.Wait()}
使用context
func con(ctx context.Context) {defer wg.Done()LOOP:for {fmt.Println("执行任务")time.Sleep(time.Millisecond * 500)select {case <-ctx.Done():break LOOPdefault:}}}func main() {ctx, cancel := context.WithCancel(context.Background())wg.Add(1)go con(ctx)time.Sleep(time.Second * 5)cancel()wg.Wait()}
使用context与channel的区别
实际上原理没什么区别!哈哈哈哈
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {if parent == nil {panic("cannot create context from nil parent")}c := newCancelCtx(parent)propagateCancel(parent, &c)return &c, func() { c.cancel(true, Canceled) }}
从WithCancel方法看。通过传递ctx 可以将gouroutine串起来。
func propagateCancel(parent Context, child canceler) {done := parent.Done()if done == nil {return // parent is never canceled}select {case <-done:// parent is already canceledchild.cancel(false, parent.Err())returndefault:}if p, ok := parentCancelCtx(parent); ok {p.mu.Lock()if p.err != nil {// parent has already been canceledchild.cancel(false, p.err)} else {if p.children == nil {p.children = make(map[canceler]struct{})}p.children[child] = struct{}{}}p.mu.Unlock()} else {atomic.AddInt32(&goroutines, +1)go func() {select {case <-parent.Done():child.cancel(false, parent.Err())case <-child.Done():}}()}}
其内部操作实际上还是借用了channel
Done() <-chan struct{} // Done 方法传递一个空的struct
无论是channel还是全局变量。又或者是channel 标志是布尔,是struct,是int .再处理goroutine这一件事上,不同人解决办法思路都不一样。而go 提供优雅的解决办法,希望能统一风格,一如代码格式化。
