从python角度,理解进程,线程,协程.md

写在前面

文中有较多的内容为转载,尽量指出转载来源。

1 进程(process)

定义:进程是正在运行程序的实例。
如chrome 进程的三种状态:

  • 就绪态
  • 执行态
  • 阻塞态

进程是基于计算机系统的异常。进程切换是需要保存上下文环境(一些寄存器,以及栈的信息。 子进程和父进程具有相同的文件描述符。 不同的进程具有不同的地址空间,变量无法共享。调度有操作系统完成。process 由 process control block (PCB)控制 ;。

2 线程(thread)

一个进程,包含多个线程 线程是一种轻量进程,实际上在linux内核中,两者几乎没有差别,除了一点——线程并不产生新的地址空间和资源描述符表,而是复用父进程的。线程的调度和进程一样,都必须陷入内核态。调度有操作系统完成(thread 由 thread control blocks (TCBs)控制)。 线程模型主要通过陷入切换上下文。。

多线程的地址空间.png

3 协程(coroutine)

一个线程,包含多协程。协程由应用程序实现调度,线程由操作系统实现调度,不需要陷入内核

3.1 函数调用[2]

函数,所有语言中都是层级调用,比如A调用B,B在执行过程中又调用了C,C执行完毕返回,B执行完毕返回,最后是A执行完毕。
函数的调用 是通过栈来实现的,一个线程就是执行一个子函数 栈帧保存了给出代码的的信息和上下文,其中包含最后执行的指令,全局和局部命名空间,异常状态等信息。f_valueblock保存了数据,b_blockstack保存了异常和循环控制方法

def foo():

 x = 1

 def bar(y):

 z = y + 2 # <--- (3) ... and the interpreter is here.

 return z

 return bar(x) # <--- (2) ... which is returning a call to bar ...

foo() # <--- (1) We're in the middle of a call to foo ...

那么,相应的调用栈如下,一个py文件,一个类,一个函数都是一个代码块,对应者一个Frame,保存着上下文环境以及字节码指令。

c ---------------------------

a | bar Frame | -> block stack: []

l | (newest) | -> data stack: [1, 2]

l ---------------------------

 | foo Frame | -> block stack: []

s | | -> data stack: [<Function foo.<locals>.bar at 0x10d389680>, 1]

t ---------------------------

a | main (module) Frame | -> block stack: []

c | (oldest) | -> data stack: [<Function foo at 0x10d3540e0>]

k ---------------------------

携程看起来像函数,但是内部可以中断。举例如

def A():

 print '1'

 print '2'

 print '3'

def B():

 print 'x'

 print 'y'

 print 'z'

假设程序是由携程执行的,,在执行A的过程中,可以随时中断,去执行B,B也可能在执行过程中中断再去执行A,结果可能是:

1

2

x

y

3

z

3.2 协程的实现 [3]

  • 并发模型


    image.png

    并发系统从本质上讲,是一系列独立的执行单元(routine)在调度器的调度之下交替执行。与线程相比,协程并发模型与其最大不同之处在于:协程由应用程序实现调度,线程由操作系统实现调度

由于协程作为执行单元并发执行时,会因为主动放弃执行权限而被挂起,调度系统必须同时维护多个函数执行上下文,以实现非本地跳转(non-local jump)。

C-Python解释器栈结构以及它是如何工作的。

def a(x):    
  b(x + 1)
def b(x):
  c(x * x)
def c(x):
    print 'x=',x
 a(42)

在CPython shell中执行上面这段代码时,Python-Stack与 C-Stack结构如下图。

image.png

Python虚拟机以eval_code2作为解释函数执行a时,首先通PyFrame_New构造a的栈帧frame-a并返回eval_code2,然后执行a对应的Python代码。由于a嵌套调用b,此时解释器递归调用eval_code2并重复之前过程执行b,从而形成C-stack和由PyFrameObject构成的python-stack。

范式转换对于stackfull的标准Python而言,实现协程并发的核心在于将Python-Stack与 C-Stack解耦,这种改变Python解释器执行过程的方法也被称作范式转换。要点可以归纳为以下三个方面:

  • 1.函数栈帧执行时机
    解释器执行Python函数的标准范式是:为函数的PyCodeObject
    构造一个函数栈帧PyFrameObject并附带所有参数,最后通过eval_code2
    解释执行相应的函数体直到其返回。然而,以正确的调用顺序执行所有的函数栈帧并不意味着我们必须在当前C-stack嵌套层级中执行eval_code2。如果我们能够避免与C-stack相关的所有后续操作,就可以在函数栈帧执行前实现C-stack的退栈操作,从而达到解耦的目的。
  • 2.参数生命周期
    在标准python中,函数参数的引用由其上层调用者持有。这意味着只有下层函数返回后,其参数元组的引用才能被上层函数销毁。
    现在,让我们换一种思维方式。很明显,函数参数应该与函数栈帧有着相同的生命周期,参数元组的引用也应该同函数栈帧一起被销毁。所以,我们在PyFrameObject结构中添加对参数元组的引用,就可以实现范式的转换。
  • 3.系统状态
    在标准python中,执行一个函数栈帧后的返回值会存在两种情况:
    • 返回PyObject:代表函数正常执行。

    • 返回NULL:代表函数抛出异常。

      基于这两种基本系统状态,添加一个特殊的返回值类型Py_UnwindToken
      作为第三种系统状态,这样我们便可以在下层栈帧被执行之前实现C-stack
      退栈操作。
      由于Py_UnwindToken与其他Python对象兼容,这一范式的转换对于大部分相关代码并不可见,我们只需要对执行栈帧的C函数做出修改即可。Return Value系统状态:

    • NULL:函数执行异常

    • Py_UnwindToken:调度函数栈帧

    • Other PyObject:作为正常结果返回

[2] 生成器的源码分析 http://www.cnblogs.com/coder2012/p/4990834.html
[3] Stackless Python 探秘 http://shymonk.com/posts/2016/06/stackless-python-tan-mi/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,377评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,390评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,967评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,344评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,441评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,492评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,497评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,274评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,732评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,008评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,184评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,837评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,520评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,156评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,407评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,056评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,074评论 2 352

推荐阅读更多精彩内容