GO篇|协程和通道

254 阅读2分钟

这是我参与更文挑战的第8天,活动详情查看: 更文挑战

学习协程前,可以先了解进程,线程的相关内容

goroutine

Go语言中,没有线程概念,只有协程goroutine。

go语言的并发是由go自己决定,对于开发者是透明的,仅需编码时告知启动几个goroutine即可,其余均不关心

和线程相比:

  • 协程更轻量,一个程序可以启动成千上万个goroutine
  • 协程是被go runtime 调度执行,线程,由操作系统调度执行,。

启动协程:使用关键字go 启动

go function()

channel

解决多个协程之间的通信,主要通过使用channel通道

声明channel,使用make函数

// 声明一个channel变量,其数据都是string
ch := make(chan string)

关键字chan,表示channel类型,是一个集合类型,

chan的操作只有两种:

接收:获取chan中的值,操作符为<- chan

发送:向chan发送值,把值放在chan中,操作符为chan<-

channel类似是goroutine的一个管道,一个往管道里发送数据,另一个从管道中取数据,如果在管道中获取不到数据,则将等到取到数据为止

  • 无缓冲channel

    • 声明一个无容量的channel,只起到传输数据的作用
    • 无缓冲channel的发送和接收操作时同时进行,也称为同步channel
  • 有缓冲channel

    • 指定channel容量大小,创建一个有缓冲channel

      ch := make(chan int,5)
      
    • 有缓冲channel的内部有一个缓冲队列

    • 发送操作向队列尾部插入元素,队列满时则阻塞等待,直到有协程从队列中接收数据从而释放队列空间

    • 接收操作从队列的头部获取元素并把它从队列中删除,队列为空时则阻塞等待,直到有协程向队列中发送数据

    ch := make(chan int,6)
    
    cap(ch) // 获取chan 长度
    close(ch) //关闭chan
    
  • 单向channel

场景:限制一个channel只接收不发送,或者只发送不接收

select

类似switch,但又有些不同。select只能用于信道channel,所以在这里提及select

select的case后带的是channel的操作,而不是判断条件。

  • select语句只能用于信道channel的读写操作

  • select的case条件是并发执行,会先执行操作成功的那个case先执行,如果多个同时返回时,则随机选择一个执行

  • 如果case条件语句中,存在通道值为nil的读写操作,则该分支会被忽略

  • 对于空select{},会引起死锁

  • 对于for的select{},可能会引起cpu占用过高的问

    ch := make(chan int , 10)
    
    for i := 0; i < size; i++ {
      ch <- 1
    }
    
    select {
      case 1==1:   // 此处运行时会报错
      	fmt.Println("equal")
      case v:= <-ch
       fmt.Print(v)
      default:
      fmt.Println("none")
    }
    
    select {
      case 1==1:   // 此处运行时会报错
      	fmt.Println("equal")
      case v:= <-ch
       fmt.Print(v)
      case b:= <-ch
       fmt.Print(b)
      default:
      fmt.Println("none")
    }