
python标准库中没有直接对应go语言`select`语句的并发原语。本文将探讨如何利用python的`threading`模块和`queue.queue`来实现类似go `select`的功能,即同时监听多个通信通道并处理首先就绪的事件。我们将通过逐步构建和优化代码示例,展示如何模拟go的“选择”机制,并讨论两种语言在并发模型上的关键差异与注意事项。
Go语言的select语句是其并发模型中的一个核心特性,它允许一个 Goroutine 等待多个通信操作。当select语句中的某个case可以执行时,它就会执行该case。如果多个case同时就绪,Go会随机选择其中一个执行。这使得 Goroutine 能够以非阻塞的方式与多个通道进行交互,从而实现复杂的并发协调逻辑。
以下是一个Go语言select语句的经典示例:
package main
import "fmt"
func main() {
c1 := make(chan int)
c2 := make(chan int)
quit := make(chan int)
// Goroutine 1: 向 c1 发送数据,最后向 quit 发送信号
go func() {
for i := 0; i < 10; i++ {
c1 <- i
}
quit <- 0
}()
// Goroutine 2: 向 c2 发送数据
go func() {
for i := 0; i < 2; i++ {
c2 <- i
}
}()
// 主 Goroutine 使用 select 监听多个通道
for {
select {
case <-c1:
fmt.Println("Received value from c1")
case <-c2:
fmt.Println("Received value from c2")
case <-quit:
fmt.Println("quit")
return // 收到 quit 信号后退出
}
}
}这段Go代码创建了两个 Goroutine 分别向c1和c2通道发送数据,并在c1发送完毕后向quit通道发送信号。主 Goroutine 使用select语句同时监听这三个通道,并根据哪个通道首先就绪来打印相应的消息,直到收到quit信号后程序终止。
由于Python没有内置的通道(channel)和select原语,我们需要借助threading模块实现并发,并使用queue.Queue作为通信通道。模拟Go的select核心思想是创建一个中心化的队列,所有被监听的“通道”都将其消息转发到这个中心队列,主线程再从中心队列中获取消息并判断来源。
立即学习“Python免费学习笔记(深入)”;
首先,我们尝试直接翻译Go的示例逻辑到Python。
import threading
import queue
def main():
# 模拟Go的通道,使用queue.Queue
c1 = queue.Queue(maxsize=0)
c2 = queue.Queue(maxsize=0)
quit = queue.Queue(maxsize=0)
# 模拟Go的 Goroutine 1
def func1():
for i in range(10):
c1.put(i)
quit.put(0) # 完成后发送退出信号
threading.Thread(target=func1).start()
# 模拟Go的 Goroutine 2
def func2():
for i in range(2):
c2.put(i)
threading.Thread(target=func2).start()
# 创建一个中心队列,用于汇总所有通道的消息
combined = queue.Queue(maxsize=0)
# 定义一个函数,用于监听指定队列并将消息转发到 combined 队列
def listen_and_forward(source_queue):
while True:
# 获取消息,并将其与源队列一起放入 combined 队列
combined.put((source_queue, source_queue.get()))
# 为每个被监听的队列启动一个守护线程进行转发
t1 = threading.Thread(target=listen_and_forward, args=(c1,))
t1.daemon = True # 设置为守护线程,主程序退出时自动终止
t1.start()
t2 = threading.Thread(target=listen_and_forward, args=(c2,))
t2.daemon = True
t2.start()
t_quit = threading.Thread(target=listen_and_forward, args=(quit,))
t_quit.daemon = True
t_quit.start()
# 主循环从 combined 队列获取消息
while True:
which_queue, message = combined.get() # 获取 (源队列, 消息) 元组
if which_queue is c1:
print('Received value from c1')
elif which_queue is c2:
print('Received value from c2')
elif which_queue is quit:
print('Received value from quit')
return # 收到退出信号,主程序终止
if __name__ == '__main__':
main()在这个实现中,我们为每个需要监听的queue.Queue(模拟Go的通道)都启动了一个独立的守护线程listen_and_forward。这些守护线程负责从各自的源队列中获取消息,然后将消息连同源队列的引用一起打包成元组(source_queue, message),并放入一个公共的combined队列。主线程则持续从combined队列中取出元组,通过检查source_queue的引用来判断消息的来源,并执行相应的逻辑。
注意:此方法与Go的select在处理“多个case同时就绪”时的行为有所不同。Go会随机选择一个,而Python的combined队列会按照消息到达的顺序进行处理,即“先到先得”。
为了使这种“多队列选择”的模式更易于复用,我们可以将其封装成一个生成器函数。
import threading
import queue
def select(*queues):
"""
模拟Go语言的 select 语句,监听多个 queue.Queue 对象。
当任一队列有新消息时,yield (源队列, 消息) 元组。
"""
combined = queue.Queue(maxsize=0)
def listen_and_forward(source_queue):
while True:
combined.put((source_queue, source_queue.get()))
# 为每个传入的队列启动一个守护线程进行转发
for q in queues:
t = threading.Thread(target=listen_and_forward, args=(q,))
t.daemon = True
t.start()
# 作为生成器,持续从 combined 队列中 yield 消息
while True:
yield combined.get()
def main_with_select_helper():
c1 = queue.Queue(maxsize=0)
c2 = queue.Queue(maxsize=0)
quit = queue.Queue(maxsize=0)
def func1():
for i in range(10):
c1.put(i)
quit.put(0)
threading.Thread(target=func1).start()
def func2():
for i in range(2):
c2.put(i)
threading.Thread(target=func2).start()
# 使用封装后的 select 函数
for which_queue, msg in select(c1, c2, quit):
if which_queue is c1:
print('Received value from c1')
elif which_queue is c2:
print('Received value from c2')
elif which_queue is quit:
print('Received value from quit')
return # 收到退出信号,主程序终止
if __name__ == '__main__':
main_with_select_helper()通过select生成器函数,我们可以更简洁地在主循环中实现多队列监听。select函数负责启动所有的转发线程,并提供一个迭代器,每次迭代都会返回一个来自就绪队列的消息。
尽管上述Python实现能够模拟Go select的基本功能,但两者之间存在一些关键差异和需要注意的事项:
选择策略不同:
消息丢失风险:
Python GIL的影响:
守护线程的重要性:
在Python中模拟Go语言的select并发模式,主要通过结合threading.Thread和queue.Queue来实现。这种模式允许Python程序同时监听多个通信通道,并在任一通道就绪时进行处理,从而实现复杂的并发协调逻辑。虽然Python的实现与Go的select在某些细节(如多就绪通道的选择策略)上有所不同,且受限于GIL,但对于许多I/O密集型并发场景,这种模式提供了一种有效且可读的解决方案。在实际应用中,开发者需要根据具体需求权衡其优缺点,并注意消息处理的完整性和线程生命周期的管理。
以上就是在Python中模拟Go语言的select并发模式的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号