1. package main
  2. import (
  3. "fmt"
  4. "runtime"
  5. "sync"
  6. "time"
  7. )
  8. var counter int = 0
  9. var lock *sync.Mutex
  10. func add(a, b int) {
  11. c := a + b
  12. lock.Lock()
  13. counter++
  14. lock.Unlock()
  15. fmt.Printf("%d: %d + %d = %d\n", counter, a, b, c)
  16. }
  17. func main() {
  18. startTime := time.Now()
  19. lock = &sync.Mutex{}
  20. for i := 0; i < 10; i++ {
  21. go add(1, i)
  22. }
  23. for {
  24. lock.Lock()
  25. c := counter
  26. lock.Unlock()
  27. runtime.Gosched()
  28. if c >= 10 {
  29. break
  30. }
  31. }
  32. endTime := time.Now()
  33. consume := endTime.Sub(startTime).Milliseconds()
  34. fmt.Println("程序执行耗时(s):", consume)
  35. }

代码分析

程序启动后使用go关键词启动了十个携程,携程执行方法add。在方法add中我们对变量counter进行一次自增,为了保证counter++的并发安全使用了sync.Mutex对操作进行了加锁确保原子性,保证了并发时间内只有一个携程来更新或者读取它。我们在主协程中通过一个死循环来判断 counter的值,只有当它大于等于 10 时,才退出循环,进而退出整个程序。
代码也因此变得更复杂,更难以维护,这还只是个简单的加法运算实现,就要写这么多代码,要引入共享变量,还要引入互斥锁来保证操作的原子性,对于更加复杂的业务代码,如果到处都要加锁、解锁,显然对开发者和维护者来说都是噩梦,Go 语言既然以并发编程作为语言的核心优势,当然不至于将这样的问题用这么繁琐的方式来解决。