文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Python 的 Generator 和 Go 的 Concurrency Pattern

2024-12-03 05:09

关注

这两个概念都是为了 producer-consumer 模式的编程方便发明的。Python 的 generator 和 iterator 以及 iterable objects 一脉相承。Go 出现比 Python 晚,解决同样的编程便捷性问题,用 channel 和 goroutine 两个概念。

[[399246]]

Go 的做法

Go 的做法比较容易理解,因为和教材里的概念一致:producer 和 consumer 各自是一个 goroutine,而一个 goroutine 是一种 green thread —— 自己放弃执行,让其他 gorotine 有机会占用 CPU,而不依赖一个 preemption 机制(比如 OS kernel)来强制休眠当前 thread 以腾出 CPU 给其他 thread。

producer 把数据写入一个 channel,consumer 从这个 channel 里读。一个 channel 就是一个 blocking queue,可以有一个 buffer。读可以通过 loop 语法。比如

 

  1. package main 
  2.  
  3. func producer(n int) chan int { 
  4.     ch := make(chan int
  5.     go func() { // This goroutine is the producer 
  6.         for i := 0; i < n; i++ { 
  7.             ch <- i 
  8.         } 
  9.         close(ch) 
  10.     }() 
  11.     return ch 
  12.  
  13. func main() { // the main goroutine is the consumer 
  14.     for i := range producer(5) { 
  15.         println(i) 
  16.     } 

请注意,上述写法让一个 Go 函数创建和返回一个 channel,同时这个 Go 函数启动一个“发射后不管”的 producer goroutine —— 这是标准 Go 做法,不太符合 C/C++ 的习俗 —— (1)创建 channel(2)启动 producer 和 consumer threads。这是因为 C/C++ 不支持 high-order functions,或者叫 functionals。具体请参见我的这个回答 什么是函数式编程思维? 这个 Go pattern 和 Python 习俗一致,因为这俩都是 functional programming languages。

Python 的做法

上述 Go 的 producer 非常接近 Python 的 generator 的写法 —— 两点区别,都是 Python 解释器代劳的结果:

对应的 Python generator 如下

 

  1. from typing import Iterator 
  2.  
  3. def producer(n: int) -> Iterator[int]: 
  4.   for i in range(n): 
  5.     yield i 
  6.  
  7. for i in producer(5): 
  8.   print(i) 

比较

Python 的 producer 不是一个函数,因为里面没有 return,而是一个 generator,因为里面有 yield。一个函数返回一个值。而一个 generator 返回一个 iterator。

Go 的 producer 是一个函数,返回一个 channel。Go 里没有 generator 这样的“新概念”。

上面 Python generator 里的代码和 Go producer 里启动的 goroutine 的代码几乎完全一样,只是把 ch <- i 换成了 yield i。

那么 Python generator 返回的 iterator 到底是个啥呢?其实就是那个 Go channel,或者叫 blocking queue 的。从这个角度看,Python generator 又是一个函数了,返回一个 blocking queue。

Python 里最常用的 generator 莫过于 range —— 上例中也出现了。所以上例中,其实调用 range 的时候,已经创建了一个 Python thread 往 range 返回的 blocking queue 里写数字。而 producer 只是从这个 queue 里取出数字,再 yield 到 producer 创建的第二个 queue 里,让 for i in producer(5) 这一行(由 main thread 执行)去读。

这样一串三个 Python threads,通过两个 queues 连成一串,就是 Rob Pike 在著名幻灯片 https://talks.golang.org/2012/concurrency.slide#1 里展示 Go concurrency pattern 里的 pipeline:

不过这里有一个区别,goroutines 是可以并行执行的,如果我们电脑里有多个 CPU cores。不过,Python threads 虽然就是 OS thread 却受制于 Python 的 GIL,所以任何时候只有一个 Python 在执行中,即使我们有很多 CPU cores。请看https://www.zhihu.com/pin/1343421894465474560

Occam's Razor

我们设计系统的时候经常需要遵循一个哲学原则 Occam's Razor —— 能达到目的的各种手段里我们选择最简单的那个。这也是本专栏名字的由来。在汉语里,这个原则(philosophical principle)叫“删繁就简三秋树”。如果做不到,必然积累还不完的技术债,以至于不可能“领异标新二月花”。

对比上面 Go 和 Python 两个例子,显然 Python 例子的代码更简单。那么是不是就说明 Python 语言的设计比 Go 更加符合 Occam's Razor 的原则了呢?

恐怕并不尽然。虽然 Python 代码简短,但是需要用户理解更多概念(generator,iterator,以及它们和 functions 以及 queues 的潜在关系)—— 这也是一种开销。

这里只是提醒大家关注保持设计的简洁。不在于挖坑比较 Python 和 Go 语言。如果回复有涉及这样比较的,恕删。:-)

来源:知乎内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯