系统学习Python——并发模型和异步编程:基础实例-[使用进程实现旋转指针]

分类目录:《系统学习Python》总目录


在讨论线程以及如何避免GIL的过程中,Python贡献者Michele Simionato发布了一个示例,可以看作演示并发的"Hello World"示例,即能展示Python"一心二用"最简单的程序。Simionato的程序使用的是multiprocessing,经过我们修改,又分别实现了使用threading和asyncio的版本。

multiprocessing包支持在单独的Python进程而非线程中运行并发任务。创建multiprocessing.Process实例后,一个全新的Python解释器以子进程的形式在后台启动。由于每个Python进程都有自己的GIL,因此程序可以使用所有可用的CPU核,但最终还是取决于操作系统的调度程序。后续的文章我们会讲具体影响,对这个简单的程序来说没什么实质差别。本文的目的是介绍multiprocessing包,展示它的API与threading的API的对应关系,方便我们把使用线程的简单程序改用进程实现。我们还是以相同的示例来讲解:启动一个函数,阻塞3秒,期间在终端展示字符动画,让用户知道程序正在运转,没有停滞。这个脚本在界面上的相同位置依次显示字符串\|/-中的各个字符,实现旋转指针动画。当缓慢的计算结束后,旋转指针那一行内容清空,显示结果:Answer: 42。下面的代码是进程multiprocessing版本:

复制代码
import itertools
import time
from multiprocessing import Process, Event  # multiprocessingAPI基本模仿threading API,不过类型提示和Mypy还是揭示了一处区别:multiprocessing.Event是函数(threading.Event是类)​,返回synchronize.Event实例,因此还需要导入multiprocessing.synchronize才能编写类型提示
from multiprocessing import synchronize

def spin(msg: str, done: synchronize.Event) -> None:  # 与threading版本一致
    for char in itertools.cycle(r'\|/-'):
        status = f'\r{char} {msg}'
        print(status, end='', flush=True)
        if done.wait(.1):
            break
    blanks = ' ' * len(status)
    print(f'\r{blanks}\r', end='')

def slow() -> int:  # 与threading版本一致
    time.sleep(3)
    return 42

def supervisor() -> int:
    done = Event()
    spinner = Process(target=spin, args=('thinking!', done))  # Process类的基本用法与Thread相似。
    print(f'spinner object: {spinner}')  # spinner.parent是创建Process对象的进程的进程ID。
    spinner.start()
    result = slow()
    done.set()
    spinner.join()
    return result

def main() -> None:
    result = supervisor()
    print(f'Answer: {result}')

if __name__ == '__main__':
    main()

threading和multiprocessing的API基本相同,但是实现方式差别很大,而且为了处理多进程编程增加的复杂度,multiprocessing的API更多。例如,把线程换成进程后,一个难点是如何在被操作系统隔离且无法共享Python对象的进程之间通信。为此,跨进程传递的对象需要序列化和反序列化,这样一来开销就增加了。在上述示例中,跨进程传递的数据只有Event状态。在multiprocessing模块底层的C代码中,Event状态通过操作系统底层信号量实现。

从Python 3.8开始,标准库提供了multiprocessing.shared_memory包,但是不支持用户定义类的实例。除了原始字节,这个包还允许进程共享一个ShareableList。这是一个可变序列类型,存放固定数量的项,项的类型可以是int、float、bool和None,以及单项不超过10MB的str和bytes。

参考文献:

1 Mark Lutz. Python学习手册M. 机械工业出版社, 2018.

2 卢西亚诺·拉马略.流畅的Python 第2版(全2册) 编程语言M.人民邮电出版社,2023.

相关推荐
2601_967987373 分钟前
终端 IDE 卷到了新高度:个人免费,符号索引快 65%,内存省 95%
ide·python·免费·性能·rune
全栈陈序员7 分钟前
【Python】基础语法入门(三十一)—— API 开发:用 FastAPI 构建 RESTful 接口
python·restful·fastapi
懂点Tech8 小时前
ctfshow misc28:GIF 高度修改解题与脚本实现
开发语言·windows·python
comedate8 小时前
SMLib 的 Examples 导读:PyAPI / SM_API / SMLib 三组示例怎么读
python·cad·smlib·examples·sm_api
云和数据.ChenGuang8 小时前
Python 重写(Override)深度解析:从 MRO 到 super() 的完整机制
java·开发语言·人工智能·python·langchain·1024程序员节
川石课堂软件测试9 小时前
学习软件测试需掌握的一些知识
数据库·python·sql·功能测试·网络协议·mysql·http
万化冥合10 小时前
PyBox屏幕键盘如何辅助演示键盘操作?
python·计算机外设
车软学堂10 小时前
从测试转开发,到底要补什么
网络·人工智能·python·功能测试·单片机·车载系统·自动化
天空依然蔚蓝10 小时前
分享一个自动检测网站在线状态的python程序
python·运维开发·1024程序员节
岁月宁静10 小时前
一个 AI 项目从本地到上线,要经过哪些步骤?全流程讲清楚
python·docker·agent