开发者

Go语言如何通过通信共享内存

目录
  • 介绍
  • goroutine 和 channel
    • goroutine :
    • channel:
  • 通过通信共享内存
    • 无缓冲区 channel:
    • 缓冲区 channel:
  • 总结

    介绍

    Go 语言使用 goroutine 和 channel,可以实现通过通信共享内存。

    本文我们介绍 Go 语言怎么通过通信共享内存。

    goroutine 和 channel

    在了解 Go 语言怎么通过通信共享内存之前。我们需要先了解一些预备知识,即 goroutine 和 channel 是什么?

    goroutine :

    goroutine 编程;具有简单的模型:它是与其它 goroutine 并发运行在同一地址空间的函数。

    goroutine 是轻量级的,所有消耗几乎就只有栈空间的分配。而且栈最开始是非常小的,所以他们很廉价,仅在需要时才会随着堆空间的分配(和释放)而变化。

    摘自「Effective Go - channels[1]」。

    注意:goroutine 之所以取名为 goroutine,是因为现有的术语 - 线程、协程、进程等等 - 无法准确传达它的含义。也有些资料将 goroutine 翻译为 Go 协程或 Go 程。

    使用 goroutine 也非常简单,在函数或方法前添加 go 关键字,即可在新的 goroutine 中调用它。当调用完成后,该 goroutine 也会安静地退出。

    此外,匿名函数也可以在 gdjieEBoroutine 中调用。

    关于 goroutine 的实现原理和调度器模型 GPM,感兴趣的读者朋友们可以自行查阅相关资料。

    channel:

    我们已了解,什djieEB么是 goroutine,以及怎么使用 goroutine 调用函数或方法、匿名函数。

    但是,想要实现 goroutine 之间的通信,我们还需要了解 channel

    channel 需要使用内置函数 make 分配内存,其结果值充当了对底层数据结构的引用。如果提供了一个可选的参数,它就会为该 channel&nbjavascriptsp;设置缓冲区大小,否则,该 channel 则为无缓冲区的 channel

    关于 channel 的实现原理,感兴趣的读者朋友们可以阅读「golang 语言中的 channel 实现原理」。

    需要注意的是,两个 goroutine 之间通过无缓冲区的 channel 通信时,同步交换数据。

    作为两个 goroutine 之间的通信管道,向 channel 中发送数据的 goroutine 称为“发送者”,反之,从 channel 中接收数据的 goroutine 称为“接收者”。

    通过通信共享内存

    我们已经基本了解 Go 语言的 goroutine 和 channel,接下来我们看一下两个 goroutine 之间怎么使用 channel (无缓冲区和缓冲区)进行通信?

    无缓冲区 channel:

    示例代码:

    func main() {
     c := make(chan int) // 定义一个无缓冲区 channel
     go func() {         // 启动一个 goroutine 调用匿名函数
      fmt.Println("启动一个 goroutine 调用匿名函数")
      c <- 1 // 该 goroutine 向 channel 发送一个值(信号)
     }()
     fmt.Println("main 函数")
     out := <-c // main goroutine 从 channel 中接收一个值(信号),再未接收到值(信号)之前,一直阻塞
     fmt.Println(out) // 该打印无实际意义,仅为了读者容易理解
    }
    

    阅读上面这段代码,我们定义一个无缓冲区 channel,执行匿名函数的 goroutine 作为发送者,main goroutine 作为接收者。

    需要注意的是,无缓冲区 channel,接收者在收到值之前,发送者会一直阻塞。同理,发送者在发送值之前,接收者也会一直阻塞。

    缓冲区 channel:

    示例代码:

    func main() {
      // c := make(chan int) // 无缓冲区 channel
     c := make(chan int, 5) // 缓冲区 channel
     for i := 0; i < 20; i++ {
      c <- 1
      go func() {
       fmt.Println("do something:", i)
       <-c
      }()
     }
    
     time.Sleep(time.Second * 2) // 为了防止 main goroutine 提前退出
    }
    

    阅读上面这段代码,我们定义一个缓冲区大小为 5 的 channel,执行匿名函数的 goroutine 作为接收者,main goroutine 作为发送者。

    需要注意的是,该段代码中有 5 个执行匿名函数的 goroutine,即 N 个接收者,1 个发送者(main goroutine)。

    我们前面讲过,接收者在收到值之前会一直阻塞,而无缓冲区 channel 在接收者收到值之前,发送者会一直阻塞。

    如果我们将上面这段代码中的缓冲区 channel 换成无缓冲区 channelN - 1 个接收者在接收到值之前,发送者会一直阻塞,发送者阻塞,导致接收者一直接收不到值,也会一直阻塞,从而导致死锁。

    上面这段话有些拗口,读者朋友们可以通过运行使用无缓冲区 channel 的代码来帮助自己理解。

    我们运行使用缓冲区大小为 5 的 channel 的代码,发现代码可以正常运行,发送者和接收者之间不会产生死锁。

    这是因为缓冲区 channel,发送者仅在值被复制到缓冲区之前阻塞,如果缓冲区已满,发送者会一直阻塞,直到某个接收者取出一个值。

    回到上面这段示例代码中,执行匿名函数的 N 个 goroutine 作为接收者,在没有收到 main goroutine 发送的数据之前,一直处于阻塞状态,直到作为发送者的 main goroutine 发送数据到缓冲区 channel 中。

    读者朋友们如果仔细阅读这段代码,会发现上面这段代码虽然不会产生死锁,但是存在一个 bug

    解决方案可以阅读我们之前的一篇文章「Go 语言使用 goroutine 运行闭包的“坑”」,限于篇幅,我就不在本文中赘述了。

    总结

    本文我们介绍 Go 语言中,什么是 goroutine 和 channel,其中 channel 分为无缓冲区和缓冲区。

    在简单了解 goroutine 和 channel 后,我们又介绍怎么使用 channel,实现两个 goroutine 之间通信。

    以上就是Go语言如何通过通信共享内存的详细内容,更多关于Go语言通信共享内存的资料请关注编程客栈(www.devze.com)djieEB其它相关文章!

    0

    上一篇:

    下一篇:

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    最新开发

    开发排行榜