概述
Go 语言在并发通信方面有着独特的 CSP 并发模型。
Go 语言主要通过 Channel 实现 Goroutine 间通信,也支持共享内存和原子操作方式。
本文将通过示例阐述 Go 语言的各种并发通信方式。
主要内容包括
并发通信机制
Channel 通信详解
共享内存通信
原子操作通信
通信方式选用指南
Channel 通信示例
一、并发通信机制
Go 语言支持以下三种 Goroutine 间通信方式:
Channel 通信:Goroutine 间传递消息和数据
共享内存:通过内存共享指定数据
原子操作:利用原子操作的同步效应
每种方式都有明确的使用场景,应根据具体需求选择合适的通信方式。
二、Channel 通信详解
Channel 是 Go 语言内置的 goroutine 间同步通信机制。可以通过 Channel 在多个 goroutine 间传递消息或数据。
1. 创建方式
使用 make 创建 Channel
ch := make(chan int) //无缓冲区Channelch := make(chan int, 100) //有缓冲Channel
2. 数据传输
通过操作符<-向 Channel 发送或接收数据
ch <- 10 // 发送数据到Channelx := <- ch // 从Channel接收数据
3. 关闭 Channel
可以通过内置 close 函数关闭 Channel
close(ch)
4. 选择器
可以通过 select 选择多个 Channel 的通信
select { case <-ch1: // 操作ch1 case <-ch2: // 操作ch2 default: // 默认操作}
5. 常见通信方式
Channel 支持多种通信方式:
同步返回值
发布-订阅
工作池
三、共享内存通信
Go 程序也可以通过共享内存进行通信:
1.
// Counter包 var Value int // 共享变量 func SetValue(v int) { Value = v // 写入共享变量 } func ReadValue() int { return Value // 读取共享变量}
2. 并发安全数据结构
Go 标准库提供了许多并发安全的数据结构,如 sync.Map
var sMap sync.Map sMap.Store("key", value) // 写入数据v, _ := sMap.Load("key") // 读取数据
这类数据结构利用内部锁保证并发安全。
3. 自定义共享数据
可以通过内置原子函数从零开始构建共享数据
type atomicInt struct { val int32} func (a *atomicInt) Increase() { atomic.AddInt32(&a.val, 1)} func (a *atomicInt) Value() int32 { return atomic.LoadInt32(&a.val) }
四、原子操作通信
Go 语言提供了原子操作函数,可以被用来进行原子通信
import "sync/atomic" var flag int32 func wait() { for atomic.LoadInt32(&flag) == 0 { runtime.Gosched() }} func notify() { atomic.StoreInt32(&flag, 1) }
利用原子操作的同步效应实现 Goroutine 间通知等待。
原子函数列表:
atomic.SwapInt32
atomic.CompareAndSwapInt32
atomic.AddInt32
atomic.LoadInt32
atomic.StoreInt32
五、通信方式选用指南
不同的通信方式各有优劣:
Channel
天然同步机制
编译期检测
只适合短小消息
共享内存
适合任意数据
必须使用同步原语
存在竞争风险
原子操作
最低层次通信
必须自己实现同步逻辑
可构建高级通信方式
根据具体场景需求,比如性能、频率、数量、分布式等方面来选择最合适的通信方式。
六、Channel 通信示例
1.
func worker(in, out chan *Task) { for { t := <-in // 处理t out<- t }} func CreateWorkerPool(num int) { in := make(chan *Task) out := make(chan *Task) // 启动goroutine for i:= 0; i < num; i++ { go worker(in, out) } return in, out}
2.
type Hub struct { subs map[chan<- int]bool} func (h *Hub) subscribe() chan<- int { ch := make(chan int) h.subs[ch] = true return ch} func (h *Hub) broadcast(msg int) { for sub := range h.subs { sub <- msg }}
总结
Go 语言内置了丰富的通信方式,包括 Channel、共享内存和原子操作,每种方式都有明确的应用场景
Channel 在不同 Goroutine 间传递消息和数据,它是 Go 的默认选择。
共享内存可以在 Goroutine 间直接共享数据,但需要控制并发访问。
原子操作提供了最底层的通信方式,需要自己实现同步逻辑。
选择合适的通信方式很重要,需要根据具体问题的并发模式、性能需求、数据量等方面进行决策。
充分理解和运用这些通信机制,可以设计出简洁高效的 Go 语言并发程序。