1 goroutine退出

1.1 超时退出

  1. func main() {
  2. var wg sync.WaitGroup
  3. quit := time.Tick(time.Second * 2) // 给个定时器
  4. wg.Add(1)
  5. go func() {
  6. defer wg.Done()
  7. <-quit
  8. fmt.Println("over goroutine")
  9. return
  10. }()
  11. wg.Wait()
  12. fmt.Println("over all")
  13. }

2 context 通知退出

  1. func main() {
  2. var wg sync.WaitGroup
  3. ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
  4. defer cancel()
  5. wg.Add(1)
  6. go func() {
  7. defer wg.Done()
  8. <-ctx.Done()
  9. fmt.Println("over goroutine")
  10. return
  11. }()
  12. wg.Wait()
  13. fmt.Println("over all")
  14. }

1. 2 外部被动退出:

  1. func main() {
  2. var wg sync.WaitGroup
  3. wg.Add(1)
  4. go func() {
  5. defer wg.Done()
  6. ch := make(chan os.Signal)
  7. signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
  8. <-ch
  9. fmt.Println("over")
  10. return
  11. }()
  12. wg.Wait()
  13. }

通过两个 goroutine 来计算数字之和,在 goroutine 完成计算后,它会计算两个结果的和:


  1. package main
  2. import "fmt"
  3. func sum(s []int, c chan int) {
  4. sum := 0
  5. for _, v := range s {
  6. sum += v
  7. }
  8. c <- sum // 把 sum 发送到通道 c
  9. }
  10. func main() {
  11. s := []int{7, 2, 8, -9, 4, 0}
  12. c := make(chan int)
  13. go sum(s[:len(s)/2], c)
  14. go sum(s[len(s)/2:], c)
  15. x, y := <-c, <-c // 从通道 c 中接收
  16. fmt.Println(x, y, x+y)
  17. }

使用channel 计算fibonacci数


  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func fibonacci(n int, c chan int) {
  6. x, y := 0, 1
  7. for i := 0; i < n; i++ {
  8. c <- x
  9. x, y = y, x+y
  10. }
  11. close(c)
  12. }
  13. func main() {
  14. c := make(chan int, 10)
  15. go fibonacci(cap(c), c)
  16. // range 函数遍历每个从通道接收到的数据,因为 c 在发送完 10 个
  17. // 数据之后就关闭了通道,所以这里我们 range 函数在接收到 10 个数据
  18. // 之后就结束了。如果上面的 c 通道不关闭,那么 range 函数就不
  19. // 会结束,从而在接收第 11 个数据的时候就阻塞了。
  20. for i := range c {
  21. fmt.Println(i)
  22. }
  23. }

借助channel完成消息的完整传递

  1. package main
  2. import (
  3. "fmt"
  4. "time"
  5. )
  6. func say(s string) {
  7. for i := 0; i < 5; i++ {
  8. time.Sleep(100 * time.Millisecond)
  9. fmt.Println(s, (i+1)*100)
  10. }
  11. }
  12. func say2(s string) {
  13. for i := 0; i < 5; i++ {
  14. time.Sleep(150 * time.Millisecond)
  15. fmt.Println(s, (i+1)*150)
  16. }
  17. }
  18. func main() {
  19. go say2("world")
  20. say("hello")
  21. }

say2 只执行了 3 次,而不是设想的 5 次
在 goroutine 还没来得及跑完 5 次的时候,主函数已经退出了。

改进

引入一个信道,默认的,信道的存消息和取消息都是阻塞的,在 goroutine 中执行完成后给信道一个值 0,则主函数会一直等待信道中的值,一旦信道有值,主函数才会结束。

  1. package main
  2. import (
  3. "fmt"
  4. "time"
  5. )
  6. func say(s string) {
  7. for i := 0; i < 5; i++ {
  8. time.Sleep(100 * time.Millisecond)
  9. fmt.Println(s, (i+1)*100)
  10. }
  11. }
  12. func say2(s string, ch chan int) {
  13. for i := 0; i < 5; i++ {
  14. time.Sleep(150 * time.Millisecond)
  15. fmt.Println(s, (i+1)*150)
  16. }
  17. ch <- 0
  18. close(ch)
  19. }
  20. func main() {
  21. ch := make(chan int)
  22. go say2("world", ch)
  23. say("hello")
  24. fmt.Println(<-ch)
  25. }

使用channel将异步变为同步

**

  1. package main
  2. import (
  3. "fmt"
  4. "time"
  5. )
  6. var ch = make(chan int)
  7. func main() {
  8. for i := 1; i <= 75; i++ {
  9. go print(i)
  10. ch <- 0
  11. }
  12. time.Sleep(5 * time.Second) //wait all goroutines
  13. }
  14. func print(i int) {
  15. <-ch
  16. fmt.Println(i)
  17. }

主协程如何等其余协程完再操作

sync包和channel来解决协程同步和通讯。


  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. "time"
  6. )
  7. var wg sync.WaitGroup //定义一个同步等待的组
  8. func task(i int){
  9. fmt.Println("task...",i)
  10. //耗时操作任务,网络请求,读取文件
  11. time.Sleep(time.Second)
  12. wg.Done() //减去一个计数
  13. }
  14. func main(){
  15. for i:= 0;i<10;i++{
  16. wg.Add(1) //添加一个计数
  17. go task(i)
  18. }
  19. wg.Wait() //阻塞直到所有任务完成
  20. fmt.Println("over")
  21. }

利用缓冲信道channel协程之间通讯,其阻塞等待功能实现等待一组协程结束,不能保证其goroutine按照顺序执行

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. var ch = make(chan int,10)
  6. func task(i int){
  7. fmt.Println("task...",i)
  8. ch <- i
  9. }
  10. func main(){
  11. for i:= 0;i<10;i++{
  12. go task(i)
  13. }
  14. for i:= 0;i<10;i++{
  15. <- ch
  16. }
  17. fmt.Println("over")
  18. }

利用无缓冲的信道channel协程之间通讯,其阻塞等待功能实现等待一组协程结束,保证了其goroutine按照顺序执行

  1. package main
  2. import (
  3. "fmt"
  4. "time"
  5. )
  6. var ch = make(chan int)
  7. func task(i int){
  8. fmt.Println("task...",i)
  9. time.Sleep(time.Second)
  10. <- ch
  11. }
  12. func main(){
  13. for i:= 0;i<10;i++{
  14. go task(i)
  15. ch <- i
  16. }
  17. fmt.Println("over")
  18. }

golang两个协程交替打印1-100的奇数偶数

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func main() {
  6. chan1 := make(chan struct{}, 1)
  7. chan2 := make(chan struct{}, 1)
  8. chan_final := make(chan struct{}, 1)
  9. chan1 <- struct{}{}
  10. N := 100
  11. go func(N int) {
  12. for i := 1; i <= N; i += 2 {
  13. <-chan1
  14. fmt.Println(i)
  15. chan2 <- struct{}{}
  16. }
  17. }(N)
  18. go func(N int) {
  19. for i := 2; i <= N; i += 2 {
  20. <-chan2
  21. fmt.Println(i)
  22. if i == N {
  23. chan_final <- struct{}{}
  24. }
  25. chan1 <- struct{}{}
  26. }
  27. }(N)
  28. <-chan_final
  29. }

dog cat fish 按这个输出一百句


  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. )
  6. //dog cat fish 按这个输出一百句 第一题
  7. var wg sync.WaitGroup
  8. func main() {
  9. // counter use to count the output
  10. var counter int = 1000
  11. dogch := make(chan struct{}, 1)
  12. catch := make(chan struct{}, 1)
  13. fishch := make(chan struct{}, 1)
  14. wg.Add(3)
  15. // let run the three goroutine
  16. // the origin of the goroutine to set dogch <- struct {}{}
  17. dogch <- struct{}{}
  18. go dog(dogch, counter, catch)
  19. go cat(catch, counter, fishch)
  20. go fish(fishch, counter, dogch)
  21. wg.Wait()
  22. }
  23. func dog(dogch chan struct{}, counter int, catch chan struct{}) {
  24. dogtimes := 0
  25. for dogtimes < counter {
  26. select {
  27. case <-dogch:
  28. fmt.Println("dog")
  29. dogtimes++
  30. // let cat run
  31. catch <- struct{}{}
  32. }
  33. }
  34. wg.Done()
  35. }
  36. func cat(catch chan struct{}, counter int, fishch chan struct{}) {
  37. cattimes := 0
  38. for cattimes < counter {
  39. select {
  40. case <-catch:
  41. fmt.Println("cat")
  42. cattimes++
  43. //let fish run
  44. fishch <- struct{}{}
  45. }
  46. }
  47. wg.Done()
  48. }
  49. func fish(fishch chan struct{}, counter int, dogch chan struct{}) {
  50. fishtimes := 0
  51. for fishtimes < counter {
  52. select {
  53. case <-fishch:
  54. fmt.Println("fish")
  55. fishtimes++
  56. // let dog run
  57. dogch <- struct{}{}
  58. }
  59. }
  60. wg.Done()
  61. }
  62. //题解:
  63. // 分别使用三个goroutine代表dog cat fish,由于携程执行的无顺序性,但是题目要求顺序循环输出,
  64. // 所以,我们使用通道来实现阻塞,通过传值到通道中来实现顺序执行
  65. // 首先定义了三个有缓冲通道 ,并设置了循环的次数counter
  66. // counter use to count the output
  67. // var counter int = 3
  68. // dogch := make(chan struct{}, 1)
  69. // catch := make(chan struct{}, 1)
  70. // fishch := make(chan struct{}, 1)
  71. // 然后对于每一个goroutine来说设置一个 for循环来执行counter次,里面采用for-select-case
  72. // 如果该通道没值 则会阻塞在这个地方直到有值传进来。
  73. // // the origin of the goroutine to set dogch <- struct {}{}
  74. // dogch <- struct{}{} 这一句话是三个goroutin启动的触发条件。
  75. // 第一个goroutine有值之后,执行第一个输出 dog,然后向catch里面传值,这个时候被阻塞的第一个goroutine//
  76. // 得以激活,一次类推,直到把循环走完,每个goroutine都done了,程序结束

一个goroutine生产消息,一个消费消息

  1. func main() {
  2. ch := make(chan int)
  3. done := make(chan bool)
  4. go func() {
  5. for {
  6. select {
  7. case ch <- rand.Intn(5): // Create and send random number into channel
  8. case <-done: // If receive signal on done channel - Return
  9. return
  10. default:
  11. }
  12. }
  13. }()
  14. go func() {
  15. for i := 0; i < 5; i++ {
  16. fmt.Println("Rand Number = ", <-ch) // Print number received on standard output
  17. }
  18. done <- true // Send Terminate Signal and return
  19. return
  20. }()
  21. <-done // Exit Main when Terminate Signal received
  22. }

笔试题参考

https://www.zhihu.com/column/interview 以及这个人的知乎其他文章