Golang 协程基础知识

本文详细介绍了Go语言中的并发基础知识,包括如何启动协程、使用无缓冲和有缓冲通道进行数据通信,以及单向通道的概念。通过示例展示了如何利用select进行多路复用,实现并发任务的高效管理。强调了Go语言中提倡通过通道通信来共享内存,提高并发安全性及性能。
摘要由CSDN通过智能技术生成

启动协程 

func main() {
   go fmt.Println("飞雪无情")
   fmt.Println("我是 main goroutine")
   time.Sleep(time.Second)
}

使用管道进行协程之间数据通信

func main() {

   ch:=make(chan string)

   go func() {

      fmt.Println("飞雪无情")

      ch <- "goroutine 完成"

   }()

   fmt.Println("我是 main goroutine")

   v:=<-ch

   fmt.Println("接收到的chan中的值为:",v)

}

 

无缓冲 channel

上面的示例中,使用 make 创建的 chan 就是一个无缓冲 channel,它的容量是 0,不能存储任何数据。所以无缓冲 channel 只起到传输数据的作用,数据并不会在 channel 中做任何停留。这也意味着,无缓冲 channel 的发送和接收操作是同时进行的,它也可以称为同步 channel。

有缓冲 channel

有缓冲 channel 类似一个可阻塞的队列,内部的元素先进先出。通过 make 函数的第二个参数可以指定 channel 容量的大小,进而创建一个有缓冲 channel,如下面的代码所示:

cacheCh:=make(chan int,5)

一个有缓冲 channel 具备以下特点:

  1. 有缓冲 channel 的内部有一个缓冲队列;

  2. 发送操作是向队列的尾部插入元素,如果队列已满,则阻塞等待,直到另一个 goroutine 执行,接收操作释放队列的空间;

  3. 接收操作是从队列的头部获取元素并把它从队列中删除,如果队列为空,则阻塞等待,直到另一个 goroutine 执行,发送操作插入新的元素。

因为有缓冲 channel 类似一个队列,可以获取它的容量和里面元素的个数。如下面的代码所示:

 

单向 channel

有时候,我们有一些特殊的业务需求,比如限制一个 channel 只可以接收但是不能发送,或者限制一个 channel 只能发送但不能接收,这种 channel 称为单向 channel。

单向 channel 的声明也很简单,只需要在声明的时候带上 <- 操作符即可,如下面的代码所示:

onlySend := make(chan<- int)

onlyReceive:=make(<-chan int)

注意,声明单向 channel <- 操作符的位置和上面讲到的发送和接收操作是一样的。

在函数或者方法的参数中,使用单向 channel 的较多,这样可以防止一些操作影响了 channel。

下面示例中的 counter 函数,它的参数 out 是一个只能发送的 channel,所以在 counter 函数体内使用参数 out 时,只能对其进行发送操作,如果执行接收操作,则程序不能编译通过。

func counter(out chan<- int) {

  //函数内容使用变量out,只能进行发送操作

}

select+channel 示例

假设要从网上下载一个文件,我启动了 3 个 goroutine 进行下载,并把结果发送到 3 个 channel 中。其中,哪个先下载好,就会使用哪个 channel 的结果。

在这种情况下,如果我们尝试获取第一个 channel 的结果,程序就会被阻塞,无法获取剩下两个 channel 的结果,也无法判断哪个先下载好。这个时候就需要用到多路复用操作了,在 Go 语言中,通过 select 语句可以实现多路复用,其语句格式如下:

select {

case i1 = <-c1:

     //todo

case c2 <- i2:

	//todo

default:

	// default todo

}

 

整体结构和 switch 非常像,都有 case 和 default,只不过 select 的 case 是一个个可以操作的 channel。

小提示:多路复用可以简单地理解为,N 个 channel 中,任意一个 channel 有数据产生,select 都可以监听到,然后执行相应的分支,接收数据并处理。

有了 select 语句,就可以实现下载的例子了。如下面的代码所示:

 

func main() {

   //声明三个存放结果的channel

   firstCh := make(chan string)

   secondCh := make(chan string)

   threeCh := make(chan string)

   //同时开启3个goroutine下载

   go func() {

      firstCh <- downloadFile("firstCh")

   }()

   go func() {

      secondCh <- downloadFile("secondCh")

   }()

   go func() {

      threeCh <- downloadFile("threeCh")

   }()

   //开始select多路复用,哪个channel能获取到值,

   //就说明哪个最先下载好,就用哪个。

   select {

   case filePath := <-firstCh:

      fmt.Println(filePath)

   case filePath := <-secondCh:

      fmt.Println(filePath)

   case filePath := <-threeCh:

      fmt.Println(filePath)

   }

}

func downloadFile(chanName string) string {

   //模拟下载文件,可以自己随机time.Sleep点时间试试

   time.Sleep(time.Second)

   return chanName+":filePath"

}

总结

在这节课中,我为你介绍了如何通过 go 关键字启动一个 goroutine,以及如何通过 channel 实现 goroutine 间的数据传递,这些都是 Go 语言并发的基础,理解它们可以更好地掌握并发。

在 Go 语言中,提倡通过通信来共享内存,而不是通过共享内存来通信,其实就是提倡通过 channel 发送接收消息的方式进行数据传递,而不是通过修改同一个变量。所以在数据流动、传递的场景中要优先使用 channel,它是并发安全的,性能也不错。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值