频 道 直 达 - 新闻 - 培训 - 软件 - 教程 - 前沿 - 组网 - 系统应用 - 安全 - 编程 - 存储 - 操作系统 - 数据库 - 服务器 - 专题 - 产品 - 案例库 - 读书 - 博客 - BBS
51CTO.COM_中国最大的网络技术网站
找资料:

15.4.1 标准调度

作者: 陈儒著 出处:电子工业出版社  2008-07-23 16:46    砖    好    评论   进入论坛
阅读提示:《Python源码剖析--深度探索动态语言核心技术》第15章主要讲述的是Python 多线程机制,本小节为大家介绍的是 标准调度。

15.4 Python 线程的调度

15.4.1 标准调度

当主线程和子线程都进入了Python 解释器之后,Python 的线程之间的切换就完全由Python 的线程调度机制掌控。Python 的线程调度机制是内建在Python 的解释器核心PyEval_EvalFrameEx 中的。在分析Python 字节码解释器的框架时,我们曾给出过一个PyEval_EvalFrameEx 的框架结构,但是在那里,并没有给出线程调度机制的实现,下面列出的是加入了线程调度机制的PyEval_EvalFrameEx 的框架结构(见代码清单15-4)。

代码清单15-4

 [ceval.c]
/* Interpreter main loop */
PyObject* PyEval_EvalFrameEx(PyFrameObject *f)
{
……
why = WHY_NOT;
for (;;) {
……
if (--_Py_Ticker < 0) {
//在切换线程之前,重置_Py_Ticker 为100,为下一个线程做准备
_Py_Ticker = _Py_CheckInterval;
tstate->tick_counter++;
if (interpreter_lock) {
//[1]:撤销当前线程状态对象,释放GIL,给别的线程一个机会
PyThreadState_Swap(NULL);
PyThread_release_lock(interpreter_lock);
//[2]:别的线程现在已经开始执行了,咱们重
新再申请GIL,等待下一次被调度
PyThread_acquire_lock(interpreter_lock, 1);
PyThreadState_Swap(tstate) != NULL;
}
}
fast_next_opcode:
f->f_lasti = INSTR_OFFSET();
/* Extract opcode and argument */
opcode = NEXTOP();
oparg = 0; /* allows oparg to be
stored in a register because
it doesn't have to be remembered
across a full loop */
if (HAS_ARG(opcode))
oparg = NEXTARG();
dispatch_opcode:
switch (opcode) {
case NOP:
goto fast_next_opcode;
case LOAD_FAST:
……
}
}
其中_Py_Ticker 就是Python 内部通过软件模拟实现的时钟中断机制,而_Py_CheckInterval 保存着Python 设定的指令数,这也就是通过sys.getcheckinterval()得到的值。在初始化时_Py_Ticker 和_Py_CheckInterval 是一样的,都是100。
 [ceval.c]
int _Py_CheckInterval = 100;
volatile int _Py_Ticker = 100;

PyEval_EvalFrameEx 每执行一条字节码指令,_Py_Ticker 就将减少1;当执行了_Py_CheckInterval 条指令之后,_Py_Ticker 将减少到0,这就将进入线程调度。注意,主线程和子线程都将调用PyEval_EvalFrameEx,所以这里的描述可能会引起混乱,为了保证读者清晰地了解线程调度时所发生的一切,我们先来回忆一下thread1.py 现在所处的情景。

主线程获得了GIL,并且正在执行PyEval_EvalFrameEx 函数的代码,这时子线程在t_bootstrap 中调用PyEval_AcquireThread。通过调用PyThrad_acquire_lock 申请GIL,但是由于GIL 被主线程占有,所以子线程被挂起。

现在整个Python 的进程中,只有一个活动的线程:主线程。主线程不断执行字节码,_Py_Ticker 的值不断减少,当_Py_Ticker 的值减少到0 时,主线程在代码清单15-4 的[1]处首先将维护当前线程状态对象的_PyThreadState_Current 设置为NULL,然后释放掉GIL。注意啦,注意啦,转折点到了:这时,由于等待GIL 而被挂起的子线程被操作系统的线程调度机制唤醒,从而最终进入PyEval_EvalFrameEx。对于主线程,注意,虽然这时它已经失去了GIL,但是由于它没有被挂起,所以对于操作系统的线程调度机制,它是可以被再次切换为活动线程的。

当操作系统的调度机制将主线程切换为活动线程之后,主线程将执行代码清单15-4的[2],在PyThread_acquire_lock 中,主线程申请GIL,由于被子线程占有,主线程将自身挂起。从这时开始,操作系统的线程调度不能再将主线程切换为活动进行,除非等到子线程释放GIL 之后。而子线程进入PyEval_EvalFrameEx 之后,开始如之前主线程一般的行为,在执行了_Py_CheckInterval 条指令之后,子线程将执行代码[1],释放GIL,由此唤醒主线程。而子线程在继续执行[2]时,又将因等待GIL 将自身挂起,如此反复,直至永恒,从而完整地在Python 中实现了对多线程机制的支持。

有一点需要特别注意,实际上,Python 并非在执行了_Py_CheckInterval 条指令之后开始线程调度。从PyEval_EvalFrameEx 的代码框架以及本书第2 部分对Python 虚拟机的剖析可以看到,很多字节码执行之后都会通过goto 转移到fast_next_opcode 处继续执行,这时是不会更新_Py_Ticker 的值的;而有的字节码在执行之后则会转移到最外层的for 循环,这时是会更新_Py_Ticker 的。Python 在这一方面有一种柔性,并非立下了军令状。

下面我们通过修改Python 源代码来观察Python 虚拟机进行线程调度的情形。在ceval.c中,我们声明一个全局的整形变量counter,用于记录实际执行的字节码指令。在PyEval_EvalFrameEx 的dispatch_opcode 标记位置之后,添加代码:++counter。注意,由于Python 虚拟机在执行一些字节码,尤其是涉及条件判断的字节码时,会发生直接跳跃的动作,所以这里counter 记录的也并非真实的字节码数量,只是最接近真实字节码数量的一个值。在PyEval_EvalFrameEx 的判断“_Py_Ticker < 0”成立之后,输出counter 的值,并将counter 重新归0。为了观察标准调度,我们先将thread1.py 中的两条time.sleep语句注释掉,thread1.py 的运行结果如图15-9 所示。

可以看到,在标准调度下,在两个切换线程的输出语句之间,只有一个线程的输出结果,要么是主线程的输出结果,要么是子线程的输出结果。这充分说明了,标准调度是让一个线程充分执行,直到激发Python 的模拟时钟中断(_Py_Ticker < 0),另一个线程才能有机会执行。同时,在输出结果中,我们发现每次发生线程切换时,输出的counter的计数结果是不定的,这与前面的分析非常吻合。

 
图15-9 标准调度下的线程切换
【责任编辑:夏书 TEL:(010)68476606】

回书目   上一节   下一节
专题
Python实用指南
Head First EJB(中文版)
Python源码剖析--深度探索动态语言核心技术
ActionScript 3.0权威指南
C# 与VB.NET 网络通信开发实战
我也说两句

匿名发表

(如果看不清请点击图片进行更换)


中 国 最 大 的 网 络 技 术 网 站 ·
技 术 成 就 梦 想
订阅技术快讯
电子杂志下载
名称:SQL Server数据库管理精品黄皮书
简介:书中文章经过精挑细选,便于用户能根据自己的实际工作和学习,快速在本书寻找到相关资料。内容涵盖了SQL Server的安装与升级、语句查询、数据备份和恢复、自动化任务、数据同步、数据字典、安全和预防、性能和优化、集群等各方面应用信息,以及DBA管理人员在数据库管理工作中
名称:2007路由技术大全
简介:《2007路由技术大全》由51CTO.com网站特别策划制作,该书包括路由器技术、路由器产品、路由器配置、安全设置、路由器故障处理、路由器密码恢复,以及广大网友在实践使用中的心得经验和技巧文章,内容注重实用性,适用于初学者入门,也适合多年从业者提高,是一本实践和理论完
名称:网络安全精品应用黄皮书
简介:《2007精品网络安全黄皮书》包括了9个大类24个小类, 800余篇文章,内容包含了熊猫烧香病毒、DDOS攻击、ARP病等热点问题的介绍及解决方案。从病毒查杀、防范、系统、数据等各方面的安全设置到黑客技术的了解、防范,涉及到了安全应用的全部领域, 由浅至深内容全面。
CCNA认证考试Pass必备
CCNA认证考试Pass必备
无线网络环境
无线网络环境
无线路由器故障处理
无线路由器故障处理
· 无线路由器故障处理
· 解析35岁技术人的价值..
· 无线重中之重:安全问题
· 无线局域网基本知识
· 家庭无线局域网
· 华为七千人主动辞职规..
· 微软出价446亿美元收购..
· 虚拟化的“赤壁之战”
· Windows Server 2008专..
· 802.11n:下一代的无线..
· 脉冲无线电uwb专题
· AIX操作系统管理应用
· 云计算时代来临
· 求职必杀技 决战面试官
· 龙芯要做中国的“奔腾”
· 2008年上半年IT技术图..
ARP攻击防范与解决方案
ARP攻击防范与解决方案
SQL Server 2008/2005全解
SQL Server 2008/2005全解
SOA 面向服务架构
SOA 面向服务架构
· SOA 面向服务架构
· SQL Server 2008/2005..
· Apache技术专题
· 三层交换技术专题
· SQL Server入门到精通
· 无线网状网(MESH)
· Windows远程桌面应用
· C#技术开发指南
· Apache技术专题
· Windows集群服务应用
· C#技术开发指南
· 文档格式标准开战 OOXM..
· 路由器设置与口令恢复
· Linux 集群技术专题
· PHP开发应用手册
· SOA 面向服务架构
ARP攻击防范与解决方案
ARP攻击防范与解决方案
SQL Server 2008/2005全解
SQL Server 2008/2005全解
SQL Server入门到精通
SQL Server入门到精通
· SQL Server入门到精通
· SQL Server 2008/2005..
· SOA 面向服务架构
· Apache技术专题
· C#技术开发指南
· 三层交换技术专题
· Apache技术专题
· C#技术开发指南
· Windows远程桌面应用
· 企业数据恢复指南
· Windows集群服务应用
· 路由器设置与口令恢复
· Linux 集群技术专题
· SOA 面向服务架构
· 了解统一威胁管理(UTM)..
· 解析35岁技术人的价值..