美文网首页
Channel简易教程

Channel简易教程

作者: 危地马拉的大坑 | 来源:发表于2020-12-19 00:14 被阅读0次

不同于传统的多线程并发模型使用共享内存来实现线程间通信的方式,golang 的哲学是通过 channel 进行协程(goroutine)之间的通信来实现数据共享。这种方式的优点是通过提供原子的通信原语,避免了竞态情形(race condition)下复杂的锁机制。
channel 可以看成一个 FIFO 队列,对 FIFO 队列的读写都是原子的操作,不需要加锁。对 channel 的操作行为结果总结如下:

操作 nil channel closed channel not-closed non-nil channel
close panic panic 成功 close
ch <- 一直阻塞 panic 阻塞或成功写入数据
<- ch 一直阻塞 读取对应类型零值 阻塞或成功读取数据

读取一个已关闭的 channel 时,总是能读取到对应类型的零值,为了和读取非空未关闭 channel 的行为区别,可以使用两个接收值:

// ok is false when ch is closed
v, ok := <-ch

golang 中大部分类型都是值类型(只有 slice / channel / map 是引用类型),读/写类型是值类型的 channel 时,如果元素 size 比较大时,应该使用指针代替,避免频繁的内存拷贝开销

两个goroutine通讯

package main

import "fmt"

func SendMessage(ch chan string){
    fmt.Println("Start to send message")
    ch <- "Hello World"
    fmt.Println("Finished!!")
}

func main(){
    fmt.Println("Go Channels Tutorial")

    values := make(chan string)
    defer close(values)

    go SendMessage(values)

    val := <-values
    fmt.Println(val)
}

main方法里创建了一个string类型的Channel,实现主协程与子协程go SendMessage进行通信。主协程执行到<-values时发生阻塞,等待读取values Channel的值,而子协程执行SendMessage方法时写入values Channel。即,主协程发生阻塞,等待子协程执行完毕后再继续执行。

执行的结果如下:

~ » go run main.go
Go Channels Tutorial
Start to send message
Finished!!
Hello World

从日志可以看出,打印val语句必须在子协程go SendMessage执行完成后才执行。

如果在此基础上添加多一个协程写入values Channel会发生什么?

func SendMessage(ch chan string){
    fmt.Println("Start to send message")
    time.Sleep(1 * time.Second)
    ch <- "Hello World"
    fmt.Println("Finished!!")
}

func main(){
    fmt.Println("Go Channels Tutorial")

    values := make(chan string)
    defer close(values)

    go SendMessage(values)
    go SendMessage(values)

    val := <-values
    fmt.Println(val)
  
  time.Sleep(3 * time.Second)
}

在主协程中,启动两个子协程给Channel写数据。而在SendMessage方法里,为了达到显示效果,在写入Channel前先睡眠1秒,在主协程也添加睡眠时间。

执行日志打印如下:

~ » go run main.go
Go Channels Tutorial
Start to send message
Start to send message
Finished!!
Hello World

发现只有其中一个协程完成写入Channel的操作。因为此Channel没有设置缓存,所有只能保存一个写入值。

那么如何才能保证两个子协程能正常写入Channel呢?

有缓冲的Channel

为了保证上面的两个子协程能顺利地把值写入Channel,我们创建一个带缓冲的Channel。

func SendMessage(ch chan string){
    fmt.Println("Start to send message")
    time.Sleep(1 * time.Second)
    ch <- "Hello World"
    fmt.Println("Finished!!")
}

func main(){
    fmt.Println("Go Channels Tutorial")

    values := make(chan string, 2)
    defer close(values)

    go SendMessage(values)
    go SendMessage(values)

    val := <-values
    fmt.Println(val)

    time.Sleep(1 * time.Second)
}

新创建的Channel缓冲两个值,这样就能保证两个子协程能正常写入到Channel中。下面看看打印日志:

~ » go run main.go
Go Channels Tutorial
Start to send message
Start to send message
Finished!!
Finished!!
Hello World

如我们猜想一样,两个子协程都能顺利结束。

晚安~

相关文章

  • Channel简易教程

    不同于传统的多线程并发模型使用共享内存来实现线程间通信的方式,golang 的哲学是通过 channel 进行协程...

  • WebGL简易教程 地理地形绘制

    WebGL简易教程(一):第一个简单示例 WebGL简易教程(二):向着色器传输数据 WebGL简易教程(三):绘...

  • Dota2 AI 简易开发教程(整理)

    Dota2 AI 简易开发教程 Dota2 AI 简易开发教程(一)——选择阵容及技能使用 Dota2 AI 简易...

  • TextureView

    Android TextureView简易教程

  • 微信小程序开发

    简易教程 · 小程序

  • 学习资料(正经的

    尚硅谷B站 IT教程集合https://space.bilibili.com/302417610/channel/...

  • 小程序简易教程

    1、小程序简易教程 2、自己点开看

  • 简易教程

    开发小程序的第一步,你需要拥有一个小程序帐号。 申请账号 小程序注册链接根据指引填写信息和提交相应的资料,就可以拥...

  • 简易教程

    创建小程序实例 最关键也是必不可少的,是 app.js、app.json、app.wxss 这三个。其中,.js后...

  • Java NIO 教程(七) FileChannel

    参考:http://ifeve.com/file-channel/原文地址 目录 Java NIO教程 Java ...

网友评论

      本文标题:Channel简易教程

      本文链接:https://www.haomeiwen.com/subject/lylqnktx.html