1.7 版本时候增加此功能。提供优雅处理goroutine的协同方式

如何通知一个goroutine 需要退出了?

全局变量

  1. var notity = false
  2. var wg sync.WaitGroup
  3. func con() {
  4. defer wg.Done()
  5. for {
  6. fmt.Println("执行任务")
  7. time.Sleep(time.Millisecond * 300)
  8. if notity {
  9. break
  10. }
  11. }
  12. }
  13. func main() {
  14. wg.Add(1)
  15. go con()
  16. time.Sleep(time.Second * 2)
  17. notity = true
  18. wg.Wait()
  19. }

利用channel

  1. var wg sync.WaitGroup
  2. var notityChan = make(chan bool, 1)
  3. func con() {
  4. defer wg.Done()
  5. LOOP:
  6. for {
  7. fmt.Println("执行任务")
  8. time.Sleep(time.Millisecond * 500)
  9. select {
  10. case <-notityChan:
  11. break LOOP
  12. default:
  13. }
  14. }
  15. }
  16. func main() {
  17. wg.Add(1)
  18. go con()
  19. time.Sleep(time.Second * 5)
  20. notityChan <- true
  21. wg.Wait()
  22. }

使用context

  1. func con(ctx context.Context) {
  2. defer wg.Done()
  3. LOOP:
  4. for {
  5. fmt.Println("执行任务")
  6. time.Sleep(time.Millisecond * 500)
  7. select {
  8. case <-ctx.Done():
  9. break LOOP
  10. default:
  11. }
  12. }
  13. }
  14. func main() {
  15. ctx, cancel := context.WithCancel(context.Background())
  16. wg.Add(1)
  17. go con(ctx)
  18. time.Sleep(time.Second * 5)
  19. cancel()
  20. wg.Wait()
  21. }

使用context与channel的区别

实际上原理没什么区别!哈哈哈哈

  1. func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
  2. if parent == nil {
  3. panic("cannot create context from nil parent")
  4. }
  5. c := newCancelCtx(parent)
  6. propagateCancel(parent, &c)
  7. return &c, func() { c.cancel(true, Canceled) }
  8. }

从WithCancel方法看。通过传递ctx 可以将gouroutine串起来。

  1. func propagateCancel(parent Context, child canceler) {
  2. done := parent.Done()
  3. if done == nil {
  4. return // parent is never canceled
  5. }
  6. select {
  7. case <-done:
  8. // parent is already canceled
  9. child.cancel(false, parent.Err())
  10. return
  11. default:
  12. }
  13. if p, ok := parentCancelCtx(parent); ok {
  14. p.mu.Lock()
  15. if p.err != nil {
  16. // parent has already been canceled
  17. child.cancel(false, p.err)
  18. } else {
  19. if p.children == nil {
  20. p.children = make(map[canceler]struct{})
  21. }
  22. p.children[child] = struct{}{}
  23. }
  24. p.mu.Unlock()
  25. } else {
  26. atomic.AddInt32(&goroutines, +1)
  27. go func() {
  28. select {
  29. case <-parent.Done():
  30. child.cancel(false, parent.Err())
  31. case <-child.Done():
  32. }
  33. }()
  34. }
  35. }

其内部操作实际上还是借用了channel

  1. Done() <-chan struct{} // Done 方法传递一个空的struct

无论是channel还是全局变量。又或者是channel 标志是布尔,是struct,是int .再处理goroutine这一件事上,不同人解决办法思路都不一样。而go 提供优雅的解决办法,希望能统一风格,一如代码格式化。