
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核心思想是创建一个中心化的队列,所有被监听的“通道”都将其消息转发到这个中心队列,主线程再从中心队列中获取消息并判断来源。
首先,我们尝试直接翻译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队列会按照消息到达的顺序进行处理,即“先到先得”。
为了使这种“多队列选择”的模式更易于复用,我们可以将其封装成一个生成器函数。
MediPro企业网站管理系统
一款基于PHP+MYSQL开发的企业网站管理软件,具有灵活的栏目内容管理功能和丰富的网站模版,可用于创建各种企业网站。v5.1版本支持了PHP5+MYSQL5环境,前台网站插件开放源码,更利于个性化的网站开发。具有以下功能特点和优越性:[>]模版精美实用具有百款适合企业网站的精美模版,并在不断增加中[>]多语言支持独立语言包,支持GBK,UTF8编码方式,可用于创建各种语言的网站[&g
1
查看详情
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来实现。这种模式允许Py
thon程序同时监听多个通信通道,并在任一通道就绪时进行处理,从而实现复杂的并发协调逻辑。虽然Python的实现与Go的select在某些细节(如多就绪通道的选择策略)上有所不同,且受限于GIL,但对于许多I/O密集型并发场景,这种模式提供了一种有效且可读的解决方案。在实际应用中,开发者需要根据具体需求权衡其优缺点,并注意消息处理的完整性和线程生命周期的管理。
以上就是在Python中模拟Go语言的select并发模式的详细内容,更多请关注其它相关文章!
相关文章:
iCloud登录入口网页版 苹果iCloud官网登录
狙击外星人小游戏开始_狙击外星人小游戏立即开始
必由学官方平台入口 必由学在线课堂登录地址
新手怎么开始学化妆 零基础化妆入门教程
斑马英语APP如何开启夜间护眼阅读_斑马英语APP夜间模式与低蓝光设置教程
Angular中父组件异步更新子组件复选框状态的实践指南
在Go语言中利用后缀数组处理多字符串:实现高效文本匹配与自动补全
Descript怎样用AI剪辑自动去噪_Descript用AI剪辑自动去噪【自动降噪】
响应式容器内容自动缩放与宽高比维持教程
抖音从哪里进入网页版_抖音官方入口链接
Yandex官网搜索引擎免登录_俄罗斯Yandex一键直达入口
自定义 WooCommerce 购物车:始终显示全部交叉销售商品
React列表渲染与独立状态管理:避免全局状态影响局部更新
照顾宝贝2小游戏免费秒玩入口
Walmart退货API集成指南:PHP cURL实现与常见问题解析
LocoySpider如何部署到云服务器_LocoySpider云部署的远程配置
优酷会员付费后没到账怎么办_优酷会员充值异常及解决方法
WordPress插件开发:正确注册卸载钩子与避免常见陷阱
Node.js CSV 数据处理:基于字段空值条件过滤整条记录的策略
CSS子选择器:如何区分并样式化嵌套列表的子层级
Lar*el表单中优雅地处理“返回”按钮以规避验证:最佳实践指南
2025AO3夸克浏览器通道_AO3手机HTTPS安全入口分享
最新韩小圈网页版登录入口_官网在线观看官方链接
自定义Bag-of-Words实现:处理带负号的词汇权重
Win11怎么开启省电模式_Win11电池节电模式自动开启
mcjs网页版在线存档 mcjs云存档登录入口
如何在 Excel Online 和 Google 表格中更改日期格式
如何解决电商平台定制报价请求的“黑洞”问题,SprykerQuoteRequest模块助你提升客户体验与销售效率
J*aScript中localStorage数据的获取、清洗与格式化教程
Python vgamepad库按键模拟:正确使用XUSB_BUTTON常量
HTML空白字符处理机制:渲染、DOM与编码实践
精准捕获:如何在页面中监听除特定元素外的所有点击事件
Win11文件资源管理器卡顿怎么修 Win11重置资源管理器进程优化响应速度【修复方法】
PDO预处理语句中冒号的正确处理:区分SQL函数格式与命名占位符
Adobe PDF表单中利用J*aScript解析与格式化日期组件的教程
晋江读书网页版在线登录 晋江读书电脑版官网
Spring Boot内嵌服务器与J*a EE全栈特性:选择与部署策略
优化大型XML文件解析:基于Python流式处理的内存高效方案
TypeScript/J*aScript:高效查找数组中首个唯一ID对象
163邮箱注册官网 免费申请163个人邮箱
C++如何生成随机数_C++ random库使用方法与范围设置
高德地图公交到站提醒失败如何解决 高德提醒权限设置
解决 Express.js 中 PUT 请求密码修改失败的路由配置指南
如何优雅地扩展SprykerGlue后端API授权逻辑,使用spryker/glue-backend-api-application-authorization-connector-extension
Tailwind CSS line-clamp 布局问题解析与修复指南
J*aScript实现单选按钮与关联输入框的联动禁用教程
html怎么运行外部js文件中的函数_运html外js文件函数法【技巧】
神庙逃亡小游戏在线玩 神庙逃亡小游戏入口
漫蛙2网页版漫画入口 漫蛙漫画在线官方登录
c++中的std::forward_list和std::list有什么不同_c++ forward_list与list区别分析