Go 语言怎么通过通信共享内存?

简介: Go 语言怎么通过通信共享内存?

介绍

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

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

goroutinechannel

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

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

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

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

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

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

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

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

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

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

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

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

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

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

03

通过通信共享内存

我们已经基本了解 Go 语言的 goroutinechannel,接下来我们看一下两个 goroutine 之间怎么使用 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,接收者在收到值之前,发送者会一直阻塞。同理,发送者在发送值之前,接收者也会一直阻塞。

示例代码:

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 运行闭包的“坑”」,限于篇幅,我就不在本文中赘述了。

04

总结

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

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


目录
相关文章
|
7天前
|
存储 JSON 监控
Viper,一个Go语言配置管理神器!
Viper 是一个功能强大的 Go 语言配置管理库,支持从多种来源读取配置,包括文件、环境变量、远程配置中心等。本文详细介绍了 Viper 的核心特性和使用方法,包括从本地 YAML 文件和 Consul 远程配置中心读取配置的示例。Viper 的多来源配置、动态配置和轻松集成特性使其成为管理复杂应用配置的理想选择。
26 2
|
6天前
|
Go 索引
go语言中的循环语句
【11月更文挑战第4天】
15 2
|
6天前
|
Go C++
go语言中的条件语句
【11月更文挑战第4天】
19 2
|
9天前
|
程序员 Go
go语言中的控制结构
【11月更文挑战第3天】
86 58
|
8天前
|
监控 Go API
Go语言在微服务架构中的应用实践
在微服务架构的浪潮中,Go语言以其简洁、高效和并发处理能力脱颖而出,成为构建微服务的理想选择。本文将探讨Go语言在微服务架构中的应用实践,包括Go语言的特性如何适应微服务架构的需求,以及在实际开发中如何利用Go语言的特性来提高服务的性能和可维护性。我们将通过一个具体的案例分析,展示Go语言在微服务开发中的优势,并讨论在实际应用中可能遇到的挑战和解决方案。
|
6天前
|
Go
go语言中的 跳转语句
【11月更文挑战第4天】
14 4
|
5天前
|
JSON 安全 Go
Go语言中使用JWT鉴权、Token刷新完整示例,拿去直接用!
本文介绍了如何在 Go 语言中使用 Gin 框架实现 JWT 用户认证和安全保护。JWT(JSON Web Token)是一种轻量、高效的认证与授权解决方案,特别适合微服务架构。文章详细讲解了 JWT 的基本概念、结构以及如何在 Gin 中生成、解析和刷新 JWT。通过示例代码,展示了如何在实际项目中应用 JWT,确保用户身份验证和数据安全。完整代码可在 GitHub 仓库中查看。
20 1
|
9天前
|
Go 数据处理 API
Go语言在微服务架构中的应用与优势
本文摘要采用问答形式,以期提供更直接的信息获取方式。 Q1: 为什么选择Go语言进行微服务开发? A1: Go语言的并发模型、简洁的语法和高效的编译速度使其成为微服务架构的理想选择。 Q2: Go语言在微服务架构中有哪些优势? A2: 主要优势包括高性能、高并发处理能力、简洁的代码和强大的标准库。 Q3: 文章将如何展示Go语言在微服务中的应用? A3: 通过对比其他语言和展示Go语言在实际项目中的应用案例,来说明其在微服务架构中的优势。
|
9天前
|
Go 数据处理 调度
探索Go语言的并发模型:Goroutines与Channels的协同工作
在现代编程语言中,Go语言以其独特的并发模型脱颖而出。本文将深入探讨Go语言中的Goroutines和Channels,这两种机制如何协同工作以实现高效的并发处理。我们将通过实际代码示例,展示如何在Go程序中创建和管理Goroutines,以及如何使用Channels进行Goroutines之间的通信。此外,本文还将讨论在使用这些并发工具时可能遇到的常见问题及其解决方案,旨在为Go语言开发者提供一个全面的并发编程指南。
|
7天前
|
Go 调度 开发者
探索Go语言中的并发模式:goroutine与channel
在本文中,我们将深入探讨Go语言中的核心并发特性——goroutine和channel。不同于传统的并发模型,Go语言的并发机制以其简洁性和高效性著称。本文将通过实际代码示例,展示如何利用goroutine实现轻量级的并发执行,以及如何通过channel安全地在goroutine之间传递数据。摘要部分将概述这些概念,并提示读者本文将提供哪些具体的技术洞见。