基础

基本使用

  • 带缓冲区channel:定义声明时候制定了缓冲区大小(长度),可以保存多个数据。用于通信
    • channel :=make(chan int,3)
  • 不带缓冲区channel:只能只能存一个数据,用于两个groutine的同步,阻塞式
    • channel :=make(chan bool)
  • 只读管道 read_only := make (<-chan int)
  • 只写管道 rite_only := make (chan<- int)

    1. func main() {
    2. //定义
    3. channel :=make(chan int,3)
    4. //存数据
    5. channel <- 1
    6. channel <- 2
    7. channel <- 3
    8. //channel <- 4,报错,容量已经满了
    9. //取数据,如果只想删掉该值,直接<- channel
    10. num :=<- channel
    11. fmt.Println(num)//1
    12. channel <- 4
    13. num2,ok:=<- channel
    14. if ok{
    15. fmt.Println(num2)//2
    16. }
    17. }

    注意事项

  • 管道只能存放指定类型数据

  • 非缓冲通道上如果发生了流入无流出,或者流出无流入,就会引起死锁。
  • 当一个协程存一个协程取,虽然存的快而取得慢,导致容量占满,也不会发生错误,而是负责存的协程会堵塞在channel <- num
  • 当一个协程存一个协程取,虽然存的慢而取得快,导致channel为空,也不会发生错误,而是负责取的协程会堵塞在 num := <- channel
  • 存满了的channel就不能继续存了,当从存满了的channel取出数据后还可以继续存
  • 当要取某一个值时,要将它前面的值剔除

    1. func main() {
    2. //定义
    3. channel :=make(chan int,3)
    4. //存数据
    5. channel <- 1
    6. channel <- 2
    7. channel <- 3
    8. //取到2,得先将1剔除
    9. <- channel
    10. res,ok:=<-channel
    11. if ok{
    12. fmt.Println(res)
    13. }
    14. }

    关闭与遍历

  • 当channel关闭后,只能取不能存

  • 方法:close(channel)
  • 垃圾回收器会在 channel 不可达时回收它,即使 channel 还未关闭。
  • 使用 for range 遍历,同go程中编译器发现channel未被关闭,则会引发deadlock错误
  • 如果channel无数据可读,那么for range会处于等待状态,只有当channel关闭,for range循环才会退出

    1. func main() {
    2. //定义
    3. channel :=make(chan int,10)
    4. //存数据
    5. channel <- 1
    6. channel <- 2
    7. channel <- 3
    8. close(channel)
    9. for k :=range channel{
    10. fmt.Println(k)
    11. }
    12. }
  • 如果采用for循环已经被关闭的管道,当管道没有数据时,读取的数据是管道的默认值,并且循环不会退出。res,ok :=<- channel,其中ok为false

    1. func main() {
    2. channel :=make(chan int,10)
    3. channel <- 1
    4. channel <- 2
    5. channel <- 3
    6. close(channel)
    7. for {
    8. res,ok :=<-channel
    9. if ok {
    10. fmt.Println(res)
    11. }else {
    12. time.Sleep(time.Second)
    13. fmt.Printf("channel无数据,取到的只为%v\n",res)
    14. }
    15. }
    16. }

    goroutine&channel

    死锁现场一:

    1. func main() {
    2. ch := make(chan int)
    3. <- ch // 阻塞main goroutine, 通道被锁
    4. }
    5. 解决:
    6. func main() {
    7. ch := make(chan int)
    8. go func() {
    9. ch<-1
    10. }()
    11. res :=<- ch
    12. fmt.Println(res)
    13. }
    14. 注意:groutine一定要在主线程对channel操作前开启,不然主线程阻塞在channel操作造成死锁,
    15. 如果上面的管道是有缓冲的,则不会堵死

    死锁现场二(无缓冲channel的陷阱):

    1. func main() {
    2. cha, chb := make(chan int), make(chan int)
    3. go func() {
    4. cha <- 1 // cha通道的数据没有被其他goroutine读取走,堵塞当前goroutine
    5. chb <- 0
    6. }()
    7. <- chb // chb 等待数据的写
    8. }
    9. 解决:
    10. func main() {
    11. cha, chb := make(chan int), make(chan int)
    12. go func() {
    13. cha <- 1 // cha通道的数据没有被其他goroutine读取走,堵塞当前goroutine
    14. chb <- 0
    15. }()
    16. <- cha //将cha也读出来。因为都是无缓冲的通道,同时也要注意,要严格按照存的顺序来取
    17. <- chb // chb 等待数据的写
    18. }

生产者与消费者(1个send,1个receive)

  1. func producter(apples chan int){
  2. for i:=1;i<=50;i++{
  3. fmt.Println("生产者:我生产了第",i,"个苹果")
  4. apples <- i
  5. }
  6. close(apples)
  7. }
  8. func consumer(apples chan int,flag chan bool){
  9. for {
  10. res ,ok := <- apples
  11. if ok{
  12. fmt.Println("消费者:我吃了第",res,"个苹果")
  13. }else {
  14. break
  15. }
  16. }
  17. flag <- true
  18. close(flag)
  19. }
  20. func main() {
  21. apples := make(chan int,20)
  22. flag := make(chan bool)
  23. go producter(apples)
  24. go consumer(apples,flag)
  25. for{
  26. _ ,ok := <- flag
  27. if ok{
  28. break
  29. }
  30. }
  31. }

多协程求素数(1个send,n个receive)

  1. func PutNum(channel chan int){
  2. for i:=1;i<=100000;i++{
  3. channel <- i
  4. }
  5. close(channel)
  6. }
  7. func PrimeNum(intchan chan int,primechan chan int ,exitchan chan bool){
  8. for{
  9. res,ok :=<- intchan
  10. if ok{
  11. if res>2{
  12. flag:=true
  13. for i:=2;i<res;i++{
  14. if res % i ==0{
  15. flag=false
  16. break
  17. }
  18. }
  19. if flag{
  20. primechan <-res
  21. }
  22. }
  23. }else {
  24. break
  25. }
  26. }
  27. exitchan <-true
  28. fmt.Println("一个线程已执行完毕")
  29. }
  30. func main() {
  31. intchan :=make(chan int,1000)//数据源管道
  32. primechan :=make(chan int,1000)//结果管道
  33. exitchan :=make(chan bool,6)//退出管道
  34. go PutNum(intchan)
  35. for i:=0;i<6;i++{
  36. go PrimeNum(intchan,primechan,exitchan)
  37. }
  38. go func() {
  39. for i:=0;i<6;i++{
  40. <- exitchan
  41. }
  42. close(primechan)
  43. }()
  44. for{
  45. res,ok:=<-primechan
  46. if !ok{
  47. break
  48. }
  49. fmt.Println(res)
  50. }
  51. }

多协程求素数(n个send,n个receive)

  1. func PutNum(channel chan int ,exitintchan chan<- bool,i int){//exitintchan为只写管道
  2. rand.Seed(time.Now().UnixNano())
  3. for {
  4. value := rand.Intn(1000)
  5. if value ==i{
  6. exitintchan <-true
  7. return
  8. }else {
  9. channel<-value
  10. }
  11. }
  12. }
  13. func PrimeNum(intchan chan int,primechan chan int ,exitchan chan<- bool){
  14. for{
  15. res,ok :=<- intchan
  16. if ok{
  17. if res>2{
  18. flag:=true
  19. for i:=2;i<res;i++{
  20. if res % i ==0{
  21. flag=false
  22. break
  23. }
  24. }
  25. if flag{
  26. primechan <-res
  27. }
  28. }
  29. }else {
  30. break
  31. }
  32. }
  33. exitchan <-true
  34. fmt.Println("一个线程已执行完毕")
  35. }
  36. func main() {
  37. intchan :=make(chan int,1000)//数据源管道
  38. exitintchan :=make(chan bool,4) //数据源退出管道
  39. primechan :=make(chan int,1000)//结果管道
  40. exitchan :=make(chan bool,6)//结果退出管道
  41. for i:=0;i<4;i++{
  42. go PutNum(intchan,exitintchan,i)
  43. }
  44. go func() {
  45. for i:=0;i<4;i++{
  46. <- exitintchan
  47. }
  48. close(intchan)
  49. }()
  50. for i:=0;i<6;i++{
  51. go PrimeNum(intchan,primechan,exitchan)
  52. }
  53. go func() {
  54. for i:=0;i<6;i++{
  55. <- exitchan
  56. }
  57. close(primechan)
  58. }()
  59. for{
  60. res,ok:=<-primechan
  61. if !ok{
  62. break
  63. }
  64. fmt.Println(res)
  65. }
  66. }

future模式

将客户端请求的处理过程从同步改为异步,以便将客户端解放出来,在服务端程序处理期间可以去干点其他事情,最后再来取请求的结果。好处在于整个调用过程中不需要等待,可以充分利用所有的时间片段,提高系统的响应速度。
image.png

  1. type query struct {
  2. request chan string
  3. response chan string
  4. }
  5. func exec(q query) {
  6. go func() {
  7. sql :=<- q.request
  8. //用睡眠替代执行耗时
  9. time.Sleep(time.Second)
  10. q.response <- sql +"---我是结果"
  11. }()
  12. }
  13. func main() {
  14. q :=query{make(chan string,1),make(chan string,1)}
  15. go exec(q)
  16. q.request <- "我是请求"
  17. //用睡眠替代其他任务
  18. time.Sleep(time.Second)
  19. fmt.Println(<-q.response)
  20. }