ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Python 并发的真正高手都掌握了这四种“隐藏”模型

Python 并发的真正高手都掌握了这四种“隐藏”模型 并发的真正高手在这样的世界内里, 当你去谈论那并发编程的时候, 大多数人的首个反应便是如此。它身为语言所内置的、历经长久考验的官方模块, 几乎已然成为现代那种高并发的Web框架就像某些框架那样的基石所在。然而呢, 我却发现, 把它当作是唯一的答案, 实际上就是掉进了一个所谓的“思维陷阱”当中。在实际进行大规模应用期间, 我慢慢察觉到, 依据不一样的问题场景, trio、curio以及这几种“隐匿”的并发模型, 通常能够给出更为简洁、更为快速的解决办法。对于这篇文章而言, 我会引领你摆脱对 的唯一依赖, 深入探究这些少有人知的并发利器, 并且告知你, 真正的技术大师并非仅精通一种工具, 而是能够依照具体情形, 灵活挑选最恰当的模型。第一层走出“ 陷阱”看清它的利与弊其具备的强大这般情形是不容置疑的, 它于当前的现代范畴里, 在各种广泛的生态系统当中稳稳占据了极为关键的核心位置, 它切实有力地支撑起如同某些特定的高性能 API 以及异步 Web 服务之类的事物, 它身上有着可靠的特性同时拥有着涵盖面极为广泛庞大的生态体系做支撑, 并且还获得来自核心语言方面的支持, 而以上这些种种因素, 全部都是致使它能够成为所谓受到广泛认可的“标准”的原因所在。然而, 于我的实践当中, 其缺点可不是一般的明显。其一乃是它无比冗长, 要去达成一项简单的异步任务, 你很可能得直面繁杂的抽象内容, 像事件循环也就是 event loop 的管理, 这对初学者而言, 类似在迷宫里毫无头绪地乱转。其二是它的取消模型, 一旦想要取消一个正在运行的任务, 其处理方式常常状况百出, 这致使它在某些复杂场景里变得没那么友好了。恰恰是由于, 这般的那些局限, 我着手寻觅别的可能性, 并且探察到了, 那些隐匿于其光芒背后的, “幕后英雄”。第二层Curio——极简主义的异步之美当初, 我的头一回“顿悟时刻”, 乃是碰到了由David所开发的curio。这个叫作curio的库, 它所秉持的设计哲学, 正是那所谓的“极简主义”。它把那些沉甸甸的抽象给剥离开来, 进而为你送去了纯粹的、结构化的协程编程感受。若使用curio, 你无需手动去管理事件循环, 也不用处理复杂的回调函数。代码变得异常简洁直观, 像这样:import curio async def hello(): await curio.sleep(1) print(Hello from curio!) if __name__ __main__: curio.run(hello)通过这段代码, curio 的精髓得以清晰且明确地展示出来, 那就是不存在“回调面条”这种情况, 也没有进行烦琐不堪的循环操作。它使我明白了这样一个意味深长的重要道理, 即在需要那种纯粹状态的并发情形, 并且不依赖任何框架的时候, 简单性相较于功能而言, 具有更突出的优势。就算 curio 的生态系统相对来说规模较小, 而这种规模状况限制了它在大规模量产环境里的应用, 无可置疑的是, 它绝对是学习异步编程基础的最为优雅、最为纯粹的选择之一。第三层Trio——为人类设计的结构化并发要是讲curio属于极简主义的代表, 那trio就把这种哲学推进到了更高的层级, 也就是结构化并发。结构化并发的主旨是, 所有并发任务都得在清晰且明确的生命周期里运行, 并非毫无约束地肆意发展。Trio 达成这一目标的方式是, 引进一个称做**“育儿室”**的理念。你能够把育儿室设想成一个“任务管理器” , 所有借助育儿室开启的任务, 都会被这个育儿室“照料”起来, 直到它们全部完结 , 这就从根源上化解了诸如中常见的“悬空任务”tasks问题 , 一个任务在后台悄然运行 , 但你或许忘掉了它 , 也没法确保它在主程序退出之际被正确地清除不掉 , Trio的育儿室机制 , 保障了任务的确定性清除 , 这对于处理分布式任务而言至关重要。import trio async def worker(name): await trio.sleep(1) print(f{name} done) async def main(): async with trio.open_nursery() as nursery: for i in range(3): nursery.start_soon(worker, ftask-{i}) trio.run(main)尽管trio的采用率依旧相对而言比较小, 其生态系统同样比其他生态系统小, 然而对于那些迫切需要具备高可靠性的分布式系统或者关键任务负载来说, trio所提供的这样一种“安全网”是极不容易被忽视掉的。第四层——让旧系统“焕发新生”的老兵居然没人发现, 在某个事物悄然诞生之前, 就出现了那么一位“老兵”竟在并发领域早早地彰显卓越, 脱颖而出, 独领一方风骚, 可不是嘛。而这个它的独特之处就在于, 竟然别具蹊径采用了一种名为协作式微线程的方式来进行工作。把阻塞I/O操作转变成异步举止“神奇地”达成的那种能力, 是最让我觉得惊奇的, 是借助**“猴子补丁”-** 技术, 能自动更改标准库里边的阻塞型函数像库, 使程序在后台运行之际这些函数不会导致整个程序被阻塞。下面这段代码展示了 的强大之处import gevent from gevent import monkey; monkey.patch_all() import requests def fetch(url): return requests.get(url).text jobs [gevent.spawn(fetch, https://httpbin.org/delay/1) for _ in range(10)] gevent.joinall(jobs)同时发起 10 个网络请求的这段代码, 不需要对库做任何修改。在我的实践里, 它成了“救星”。在那些没法进行彻底重构的遗留或 Flask 应用当中, 它能作为即插即用的升级方案, 为这些老旧系统毫无痛苦地引入并发能力。然也, 此般“魔法”亦不乏其代价焉。缘其底层之实现系赖于此隐匿之“猴子补丁”, 是以当程序现异常行径之际, 调试遂变得颇为棘手也。第五层——在最底层玩转协程的底层秘密所在的武器, 恰恰就是, 它属于一种更底层的协程原语 , 从本质上来说是要在运行的时候去进行栈切换。违反了async/await语法方面的规则, 它仅仅是在各异的栈之间做跳转, 这听上去略微有点具备潜在风险的意味, 由于它给予了你过多的底层操控权。然恰恰是这般灵活性, 致使在某些特殊场景里变得无可替代, 举例而言, 于构建游戏引擎、解析器或者领域特定语言DSL解释器之际, 你得对协程的执行流拥有最为原始、最为直接的把控, 而它正是为此而诞生的。下面是一个简单的 使用示例from greenlet import greenlet def foo(): print(Foo 1) gr2.switch() print(Foo 2) def bar(): print(Bar 1) gr1.switch() print(Bar 2) gr1 greenlet(foo) gr2 greenlet(bar) gr1.switch()这段代码呈现了, 怎样以手动的方式, 在两个函数之间, 来回进行切换执行。它并不契合日常的API开发, 然而在那些有着需要精细把控协程行为的特殊情形之下, 它能够展现出无可比拟的价值。第六层如何将不同模型“混合搭配”于实际的生产环境里, 我并非单单挑选其中一种模型。相反, 我发觉把它们混合起来运用, 方可切实解决复杂的问题。我曾搭建过一个混合栈, 其样式类似“弗兰肯斯坦式”, 它将我们不同层面的需求恰当解决了:这种“混合搭配”可行的原因在于, 每一种并发模型处理了不同层面问题的挑战, 像是处理了高吞吐量的 Web 服务, Trio 提供了可靠的任务生命周期管理, 还有在不改动代码的前提下, 让旧系统也能享有并发便利的某种方式。第七层真正的“大师级”洞见并发编程在 中绝不仅仅是和多线程之间的二选一。所以, 真正称得上是技术大师的人, 并非仅仅局限于会运用这单一一种工具, 而是得去深入了解这些并发模型所蕴含的核心思想, 并且要清晰明确地知晓, 在何种场景之下, 应当选定其中哪一个。这样一种认知方面出现的转变, 将会使得你在着手解决复杂技术问题之际, 具备更为广阔的视野以及更为强大的武器储备库。期望此项文章有助于你冲破对于的思维定规, 再度审视并发编程的宽广世界。要是你对这些模型存有任何疑惑, 或者拥有自身的实践经历, 欢迎于评论区留言探讨。
返回列表