Go语言有一个适用于通道的select语句。
select语句允许goroutine等待多个通信操作。
一个select阻塞直到它的一个case可以运行,然后它执行那个case。如果有多个case准备好了,它随机选择一个。
下面的代码是否有Python等价物:
package main
import "fmt"
func main() {
c1 := make(chan int)
c2 := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
c1 <- i
}
quit <- 0
}()
go func() {
for i := 0; i < 2; i++ {
c2 <- i
}
}()
for {
select {
case <-c1:
fmt.Println("Received value from c1")
case <-c2:
fmt.Println("Received value from c2")
case <-quit:
fmt.Println("quit")
return
}
}
}
此程序的输出:
Received value from c1
Received value from c1
Received value from c2
Received value from c1
Received value from c2
Received value from c1
Received value from c1
Received value from c1
Received value from c1
Received value from c1
Received value from c1
Received value from c1
quit
8条答案
按热度按时间zsohkypk1#
这是一个相当直接的翻译,但是“如果多个就绪,选择哪个”部分的工作方式不同--它只是取第一个进来的。
基本的变化是用合并消息的线程来模拟select。如果您要经常使用这种模式,可以编写一些select代码:
但是...
请注意,这个select并不是go,虽然它对你的程序来说并不重要--一个goroutine可能会在一个通道上发送一个结果,如果我们不总是遍历select直到完成,这个结果会在select中排队并丢失!
h5qlskok2#
还可以考虑一下Benoit Chesneau的offset library,它是Go语言并发模型向Python的移植,使用了隐藏的纤程。
他在PyCon APAC 2013上就此发表了演讲:
irlmq6kh3#
您可以使用
multiprocessing.Pipe
代替chan
,使用threading.Thread
代替go
,使用select.select
代替select
。下面是一个用Python重新实现的例子:
该实现基于@托马斯的实现,但与@Thomas的实现不同,它不会产生额外的线程来执行选择。
在Linux上使用Python 2.7.13进行测试。Windows可能会有不同的行为,因为选择是一个Unixy的事情。
编辑:我添加了
len(ready) == 1
条件,这样quit只在其他管道被清空后才被处理。这在Go语言中是不需要的,因为通道的大小为零,所以func1
不能向quit_w
发送消息,直到发送给c1_w
的消息被接收 * 之后 *。感谢@Sean佩里的评论。vatpfxk54#
在Python 3.5中,有关键字
async
和await
,这使得函数可以在执行中被挂起,从而能够在evenloop上运行而不是在线程上运行。asyncio
标准库提供了一个。为了更直接地MapGo语言阻塞通道和
select
的行为,你可以使用this small library,这样你的示例代码在Python中看起来就非常相似了。eimct9ow5#
是的,goless可以实现所有功能。您可以尝试一下。
玩得开心;-)
以下是一个示例:
kiz8lqtg6#
下面是另一个例子,它试图模仿go语法:
xxls0lw87#
完整性:Go风格通道(包括工作选择)作为pygolang的一部分提供:
offset(参见https://stackoverflow.com/a/19143696/9456786)似乎也很有趣。
不幸是,goless(参见https://stackoverflow.com/a/39269599/9456786)具有弱选择实现,这通过设计does not work properly on synchronous channels来实现。
jgzswidk8#
这里有几个使用
queue.Queue
和threading.Thread
来模拟选择行为的答案,但这不是必需的。这就在队列周围添加了一个额外的信号量,更重要的是,这个信号量可以通过文件描述符来访问,这意味着你现在可以用
select.select()
来等待这个队列,所以上面使用队列和线程的例子可以在没有额外线程的情况下重写:这里的
main
函数非常类似于上面的@alkasm给出的函数,但是没有select
的自定义实现,也没有每个队列的线程来将所有单独的队列收集到一个队列中;它依赖于操作系统来告诉您队列何时有可用项。注意
os.eventfd
只在Python 3.10中被添加,但是在ctypes中实现它是相当简单的,或者在PyPI上有eventfd
包。后者也支持Windows,不像其他选项,用管道模拟eventfds。Python文档声称eventfds只在运行glibc〉= 2.8的Linux系统上可用,但是muslc也支持它们。