您所在的位置:首页 / 知识分享

Go语言并发

2019.12.31

620

Go语言的并发是基于 goroutine 的,goroutine 类似于线程,但并非线程。可以将 goroutine 理解为一种虚拟线程。Go语言运行时会参与调度 goroutine,并将 goroutine 合理地分配到每个 CPU 中,最大限度地使用 CPU 性能。

在早期 CPU 都是以单核的形式顺序执行机器指令。Go语言的祖先C语言正是这种顺序编程语言的代表。顺序编程语言中的顺序是指:所有的指令都是以串行的方式执行,在相同的时刻有且仅有一个 CPU 在顺序执行程序的指令。

随着处理器技术的发展,单核时代以提升处理器频率来提高运行效率的方式遇到了瓶颈,单核 CPU 发展的停滞,给多核 CPU 的发展带来了机遇。相应地,编程语言也开始逐步向并行化的方向发展。


Go语言正是在多核和网络化的时代背景下诞生的原生支持并发的编程语言。Go语言从底层原生支持并发,无须第三方库,开发人员可以很轻松地在编写程序时决定怎么使用 CPU 资源。

Go语言的并发是基于 goroutine 的,goroutine 类似于线程,但并非线程。可以将 goroutine 理解为一种虚拟线程。Go语言运行时会参与调度 goroutine,并将 goroutine 合理地分配到每个 CPU 中,最大限度地使用 CPU 性能


多个 goroutine 中,Go语言使用通道(channel)进行通信,通道是一种内置的数据结构,可以让用户在不同的 goroutine 之间同步发送具有类型的消息。这让编程模型更倾向于在 goroutine 之间发送消息,而不是让多个 goroutine 争夺同一个数据的使用权。


程序可以将需要并发的环节设计为生产者模式和消费者的模式,将数据放入通道。通道另外一端的代码将这些数据进行并发计算并返回结果,如下图所示。



Go语言通过通道可以实现多个 goroutine 之间内存共享


【实例】生产者每秒生成一个字符串,并通过通道传给消费者,生产者使用两个 goroutine 并发运行,消费者在 main() 函数的 goroutine 中进行处理。



  1. package main
  2. import (
  3. "fmt"
  4. "math/rand"
  5. "time"
  6. )
  7. // 数据生产者
  8. func producer(header string, channel chan<- string) {
  9. // 无限循环, 不停地生产数据
  10. for {
  11. // 将随机数和字符串格式化为字符串发送给通道
  12. channel <- fmt.Sprintf("%s: %v", header, rand.Int31())
  13. // 等待1秒
  14. time.Sleep(time.Second)
  15. }
  16. }
  17. // 数据消费者
  18. func customer(channel <-chan string) {
  19. // 不停地获取数据
  20. for {
  21. // 从通道中取出数据, 此处会阻塞直到信道中返回数据
  22. message := <-channel
  23. // 打印数据
  24. fmt.Println(message)
  25. }
  26. }
  27. func main() {
  28. // 创建一个字符串类型的通道
  29. channel := make(chan string)
  30. // 创建producer()函数的并发goroutine
  31. go producer("cat", channel)
  32. go producer("dog", channel)
  33. // 数据消费函数
  34. customer(channel)
  35. }


整段代码中,没有线程创建,没有线程池也没有加锁,仅仅通过关键字 go 实现 goroutine,和通道实现数据交换。





相关新闻

Go 每日一练之类型断言

2020.08.05

316

Go 类型断言广泛用于interfer{}类型转为其他类型(经常出现在map类型中),另外只有initerface类型才可以进行类型断言。