IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> python虚拟机原理-生成器,协程背后的运行机制 -> 正文阅读

[Python知识库]python虚拟机原理-生成器,协程背后的运行机制

程序为了实现多任务,可以用多进程,多线程。但这两种,资源耗费较高。比如多线程,每一次切换,都会有成本。从一个线程a切到另一线程b,再切回线程a,操作系统是如何记住程序a执行位置的。其实在cpu中,有专门的寄存器存放程序执行位置。等切到线程b,系统会把线程a的执行上下文信息存在内存中。这样线程间切换就会有成本。为了解决这个问题。协程被提出来。

这里提出个问题:协程在虚拟机层面的工作原理是什么?

协程的工作原理:

我们看下面代码:

def co_process(arg):
    print('task with argument {} started'.format(arg))

    data = yield 1
    print('step one finished, got {} from caller'.format(data))

    data = yield 2
    print('step two finished, got {} from caller'.format(data))

    data = yield 3
    print('step three finished, got {} from caller'.format(data))

genco1 = co_process('1')
genco2 = co_process('2')
genco3 = co_process('3')

while True:
    next(genco1)
    next(genco2)
    next(genco3)

再看它们背后的字节码:

/usr/local/bin/python3.9 /Users/tanliang/PycharmProjects/python源码剖析/compile.py?
? 1 ? ? ? ? ? 0 LOAD_CONST ? ? ? ? ? ? ? 0 (<code object co_process at 0x10acdbdf0, file "circle.py", line 1>)
? ? ? ? ? ? ? 2 LOAD_CONST ? ? ? ? ? ? ? 1 ('co_process')
? ? ? ? ? ? ? 4 MAKE_FUNCTION ? ? ? ? ? ?0
? ? ? ? ? ? ? 6 STORE_NAME ? ? ? ? ? ? ? 0 (co_process)

?13 ? ? ? ? ? 8 LOAD_NAME ? ? ? ? ? ? ? ?0 (co_process)
? ? ? ? ? ? ?10 LOAD_CONST ? ? ? ? ? ? ? 2 ('1')
? ? ? ? ? ? ?12 CALL_FUNCTION ? ? ? ? ? ?1
? ? ? ? ? ? ?14 STORE_NAME ? ? ? ? ? ? ? 1 (genco1)

?14 ? ? ? ? ?16 LOAD_NAME ? ? ? ? ? ? ? ?0 (co_process)
? ? ? ? ? ? ?18 LOAD_CONST ? ? ? ? ? ? ? 3 ('2')
? ? ? ? ? ? ?20 CALL_FUNCTION ? ? ? ? ? ?1
? ? ? ? ? ? ?22 STORE_NAME ? ? ? ? ? ? ? 2 (genco2)

?15 ? ? ? ? ?24 LOAD_NAME ? ? ? ? ? ? ? ?0 (co_process)
? ? ? ? ? ? ?26 LOAD_CONST ? ? ? ? ? ? ? 4 ('3')
? ? ? ? ? ? ?28 CALL_FUNCTION ? ? ? ? ? ?1
? ? ? ? ? ? ?30 STORE_NAME ? ? ? ? ? ? ? 3 (genco3)

?18 ? ? >> ? 32 LOAD_NAME ? ? ? ? ? ? ? ?4 (next)
? ? ? ? ? ? ?34 LOAD_NAME ? ? ? ? ? ? ? ?1 (genco1)
? ? ? ? ? ? ?36 CALL_FUNCTION ? ? ? ? ? ?1
? ? ? ? ? ? ?38 POP_TOP

?19 ? ? ? ? ?40 LOAD_NAME ? ? ? ? ? ? ? ?4 (next)
? ? ? ? ? ? ?42 LOAD_NAME ? ? ? ? ? ? ? ?2 (genco2)
? ? ? ? ? ? ?44 CALL_FUNCTION ? ? ? ? ? ?1
? ? ? ? ? ? ?46 POP_TOP

?20 ? ? ? ? ?48 LOAD_NAME ? ? ? ? ? ? ? ?4 (next)
? ? ? ? ? ? ?50 LOAD_NAME ? ? ? ? ? ? ? ?3 (genco3)
? ? ? ? ? ? ?52 CALL_FUNCTION ? ? ? ? ? ?1
? ? ? ? ? ? ?54 POP_TOP
? ? ? ? ? ? ?56 JUMP_ABSOLUTE ? ? ? ? ? 32
? ? ? ? ? ? ?58 LOAD_CONST ? ? ? ? ? ? ? 5 (None)
? ? ? ? ? ? ?60 RETURN_VALUE

Disassembly of <code object co_process at 0x10acdbdf0, file "circle.py", line 1>:
? 2 ? ? ? ? ? 0 LOAD_GLOBAL ? ? ? ? ? ? ?0 (print)
? ? ? ? ? ? ? 2 LOAD_CONST ? ? ? ? ? ? ? 1 ('task with argument {} started')
? ? ? ? ? ? ? 4 LOAD_METHOD ? ? ? ? ? ? ?1 (format)
? ? ? ? ? ? ? 6 LOAD_FAST ? ? ? ? ? ? ? ?0 (arg)
? ? ? ? ? ? ? 8 CALL_METHOD ? ? ? ? ? ? ?1
? ? ? ? ? ? ?10 CALL_FUNCTION ? ? ? ? ? ?1
? ? ? ? ? ? ?12 POP_TOP

? 4 ? ? ? ? ?14 LOAD_CONST ? ? ? ? ? ? ? 2 (1)
? ? ? ? ? ? ?16 YIELD_VALUE
? ? ? ? ? ? ?18 STORE_FAST ? ? ? ? ? ? ? 1 (data)

? 5 ? ? ? ? ?20 LOAD_GLOBAL ? ? ? ? ? ? ?0 (print)
? ? ? ? ? ? ?22 LOAD_CONST ? ? ? ? ? ? ? 3 ('step one finished, got {} from caller')
? ? ? ? ? ? ?24 LOAD_METHOD ? ? ? ? ? ? ?1 (format)
? ? ? ? ? ? ?26 LOAD_FAST ? ? ? ? ? ? ? ?1 (data)
? ? ? ? ? ? ?28 CALL_METHOD ? ? ? ? ? ? ?1
? ? ? ? ? ? ?30 CALL_FUNCTION ? ? ? ? ? ?1
? ? ? ? ? ? ?32 POP_TOP

? 7 ? ? ? ? ?34 LOAD_CONST ? ? ? ? ? ? ? 4 (2)
? ? ? ? ? ? ?36 YIELD_VALUE
? ? ? ? ? ? ?38 STORE_FAST ? ? ? ? ? ? ? 1 (data)

? 8 ? ? ? ? ?40 LOAD_GLOBAL ? ? ? ? ? ? ?0 (print)
? ? ? ? ? ? ?42 LOAD_CONST ? ? ? ? ? ? ? 5 ('step two finished, got {} from caller')
? ? ? ? ? ? ?44 LOAD_METHOD ? ? ? ? ? ? ?1 (format)
? ? ? ? ? ? ?46 LOAD_FAST ? ? ? ? ? ? ? ?1 (data)
? ? ? ? ? ? ?48 CALL_METHOD ? ? ? ? ? ? ?1
? ? ? ? ? ? ?50 CALL_FUNCTION ? ? ? ? ? ?1
? ? ? ? ? ? ?52 POP_TOP

?10 ? ? ? ? ?54 LOAD_CONST ? ? ? ? ? ? ? 6 (3)
? ? ? ? ? ? ?56 YIELD_VALUE
? ? ? ? ? ? ?58 STORE_FAST ? ? ? ? ? ? ? 1 (data)

?11 ? ? ? ? ?60 LOAD_GLOBAL ? ? ? ? ? ? ?0 (print)
? ? ? ? ? ? ?62 LOAD_CONST ? ? ? ? ? ? ? 7 ('step three finished, got {} from caller')
? ? ? ? ? ? ?64 LOAD_METHOD ? ? ? ? ? ? ?1 (format)
? ? ? ? ? ? ?66 LOAD_FAST ? ? ? ? ? ? ? ?1 (data)
? ? ? ? ? ? ?68 CALL_METHOD ? ? ? ? ? ? ?1
? ? ? ? ? ? ?70 CALL_FUNCTION ? ? ? ? ? ?1
? ? ? ? ? ? ?72 POP_TOP
? ? ? ? ? ? ?74 LOAD_CONST ? ? ? ? ? ? ? 0 (None)
? ? ? ? ? ? ?76 RETURN_VALUE
None

Process finished with exit code 0
?

分析字节码:

第1行:定义一个函数对象:co_process。

第13-15行:调用函数对象,生成generator对象。

第18-20行:18行调用next函数,进入栈帧对象。在栈帧对象中,执行栈帧对象的代码对象。当运行到YIELD_VALUE,会把相应值返回,然后退出当前栈帧对象,回到模块栈帧。这里控制权回到模块。现在执行19行,再次把相应栈帧压入虚拟机。然后执行函数代码对象,当执行到YIELD_VALUE,同样返回值 ,把控制权回到虚拟机模块级别。所以执行就在各自的生成器对象和模块间切换。实现了多任务的功能。

虚拟机通过一个while循环,重新回到18行执行时,为什么能接着第5行代码执行,好像有记忆功能似的?

其实很好实现,每个生成器对象都有一个栈帧对象,而栈帧对象维护着一个变量记录当前执行代码的位置。所以当代码重新回到18行时,再次把之前生成器栈帧压入当前栈帧,读取代码执行位置,就能接着第5行代码执行。

  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2022-10-08 20:37:01  更:2022-10-08 20:37:41 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/16 7:56:41-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码