博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python greenlet
阅读量:4030 次
发布时间:2019-05-24

本文共 3393 字,大约阅读时间需要 11 分钟。

python greenlet背景介绍与实现机制

摘要

最近开始研究Python的并行开发技术,包括多线程,多进程,协程等。逐步整理了网上的一些资料,今天整理一下greenlet相关的资料。

最近开始研究Python的并行开发技术,包括多线程,多进程,协程等。逐步整理了网上的一些资料,今天整理一下greenlet相关的资料。

 并发处理的技术背景

并行化处理目前很受重视, 因为在很多时候,并行计算能大大的提高系统吞吐量,尤其在现在多核多处理器的时代, 所以像lisp这种古老的语言又被人们重新拿了起来, 函数式编程也越来越流行。 介绍一个python的并行处理的一个库: greenlet。 python 有一个非常有名的库叫做 stackless ,用来做并发处理, 主要是弄了个叫做tasklet的微线程的东西, 而greenlet 跟stackless的最大区别是, 他很轻量级?不够, 最大的区别是greenlet需要你自己来处理线程切换, 就是说,你需要自己指定现在执行哪个greenlet再执行哪个greenlet。

greenlet的实现机制

以前使用python开发web程序,一直使用的是fastcgi模式.然后每个进程中启动多个线程来进行请求处理.这里有一个问题就是需要保证每个请求响应时间都要特别短,不然只要多请求几次慢的就会让服务器拒绝服务,因为没有线程能够响应请求了.平时我们的服务上线都会进行性能测试的,所以正常情况没有太大问题.但是不可能所有场景都测试到.一旦出现就会让用户等好久没有响应.部分不可用导致全部不可用.后来转换到了coroutine,python 下的greenlet.所以对它的实现机制做了一个简单的了解.
每个greenlet都只是heap中的一个python object(PyGreenlet).所以对于一个进程你创建百万甚至千万个greenlet都没有问题.

Python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
typedef
 
struct
 
_greenlet
 
{
PyObject_HEAD
char
*
 
stack_start
;
char
*
 
stack_stop
;
char
*
 
stack_copy
;
intptr_t 
stack_saved
;
struct
 
_greenlet
*
 
stack_prev
;
struct
 
_greenlet
*
 
parent
;
PyObject
*
 
run_info
;
struct
 
_frame
*
 
top_frame
;
int
 
recursion_depth
;
PyObject
*
 
weakreflist
;
PyObject
*
 
exc_type
;
PyObject
*
 
exc_value
;
PyObject
*
 
exc_traceback
;
PyObject
*
 
dict
;
}
 
PyGreenlet
;

每一个greenlet其实就是一个函数,以及保存这个函数执行时的上下文.对于函数来说上下文也就是其stack..同一个进程的所有的greenlets共用一个共同的操作系统分配的用户栈.所以同一时刻只能有栈数据不冲突的greenlet使用这个全局的栈.greenlet是通过stack_stop,stack_start来保存其stack的栈底和栈顶的,如果出现将要执行的greenlet的stack_stop和目前栈中的greenlet重叠的情况,就要把这些重叠的greenlet的栈中数据临时保存到heap中.保存的位置通过stack_copy和stack_saved来记录,以便恢复的时候从heap中拷贝回栈中stack_stop和stack_start的位置.不然就会出现其栈数据会被破坏的情况.所以应用程序创建的这些greenlet就是通过不断的拷贝数据到heap中或者从heap中拷贝到栈中来实现并发的.对于io型的应用程序使用coroutine真的非常舒服.

下面是greenlet的一个简单的栈空间模型(from greenlet.c)

Python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
A
 
PyGreenlet 
is
 
a
 
range
 
of
 
C
 
stack 
addresses 
that 
must 
be
saved 
and
 
restored 
in
 
such
 
a
 
way 
that 
the 
full 
range
 
of 
the
stack 
contains 
valid 
data 
when 
we 
switch 
to 
it
.
 
Stack 
layout 
for
 
a
 
greenlet
:
 
               
|
     
^
^
^
       
|
               
|
  
older 
data
   
|
               
|
               
|
  
stack
_stop
 
.
 
|
_______________
|
        
.
      
|
               
|
        
.
      
|
 
greenlet 
data
 
|
        
.
      
|
   
in
 
stack
    
|
        
.
    
*
 
|
_______________
|
 
.
 
.
  
_____________  
stack_copy
 
+
 
stack
_saved
        
.
      
|
               
|
     
|
             
|
        
.
      
|
     
data
      
|
     
|
greenlet 
data
|
        
.
      
|
   
unrelated
   
|
     
|
    
saved
    
|
        
.
      
|
      
to
       
|
     
|
   
in
 
heap
   
|
stack
_start
 
.
 
|
     
this
      
|
 
.
 
.
 
|
_____________
|
 
stack_copy
               
|
   
greenlet
    
|
               
|
               
|
               
|
  
newer 
data
   
|
               
|
     
vvv
       
|

下面是一段简单的greenlet代码.

Python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from
 
greenlet 
import
 
greenlet
 
def
 
test1
(
)
:
    
print
 
12
    
gr2
.
switch
(
)
    
print
 
34
 
def
 
test2
(
)
:
    
print
 
56
    
gr1
.
switch
(
)
    
print
 
78
 
gr1
 
=
 
greenlet
(
test1
)
gr2
 
=
 
greenlet
(
test2
)
gr1
.
switch
(
)

目前所讨论的协程,一般是编程语言提供支持的。目前我所知提供协程支持的语言包括python,lua,go,erlang, scala和rust。协程不同于线程的地方在于协程不是操作系统进行切换,而是由程序员编码进行切换的,也就是说切换是由程序员控制的,这样就没有了线程所谓的安全问题。
所有的协程都共享整个进程的上下文,这样协程间的交换也非常方便。
相对于第二种方案(I/O多路复用),使得使用协程写的程序将更加的直观,而不是将一个完整的流程拆分成多个管理的事件处理。
协程的缺点可能是无法利用多核优势,不过,这个可以通过协程+进程的方式来解决。
协程可以用来处理并发来提高性能,也可以用来实现状态机来简化编程。我用的更多的是第二个。去年年底接触python,了解到了python的协程概念,后来通过pycon china2011接触到处理yield,greenlet也是一个协程方案,而且在我看来是更可用的一个方案,特别是用来处理状态机。
目前这一块已经基本完成,后面抽时间总结一下。

总结一下:
1)多进程能够利用多核优势,但是进程间通信比较麻烦,另外,进程数目的增加会使性能下降,进程切换的成本较高。程序流程复杂度相对I/O多路复用要低。
2)I/O多路复用是在一个进程内部处理多个逻辑流程,不用进行进程切换,性能较高,另外流程间共享信息简单。但是无法利用多核优势,另外,程序流程被事件处理切割成一个个小块,程序比较复杂,难于理解。
3)线程运行在一个进程内部,由操作系统调度,切换成本较低,另外,他们共享进程的虚拟地址空间,线程间共享信息简单。但是线程安全问题导致线程学习曲线陡峭,而且易出错。
4)协程有编程语言提供,由程序员控制进行切换,所以没有线程安全问题,可以用来处理状态机,并发请求等。但是无法利用多核优势。
上面的四种方案可以配合使用,我比较看好的是进程+协程的模式。

转载地址:http://tqhbi.baihongyu.com/

你可能感兴趣的文章
POJ 3006 Dirichlet's Theorem on Arithmetic Progressions(我的水题之路——加i个d后的第几个素数)
查看>>
POJ 3030 Nasty Hacks(我的水题之路——比较大小)
查看>>
POJ 3062 Celebrity jeopardy(我的水题之路——原样输出)
查看>>
POJ 3077 Rounders(我的水题之路——高精度四舍五入)
查看>>
POJ 3086 Triangular Sums(我的水题之路——三角数累加)
查看>>
POJ 3096 Surprising Strings(我的水题之路——重点,D-pairs)
查看>>
POJ 3100 Root of the Problem(我的水题之路——取A^N最接近B的A)
查看>>
POJ 3117 World Cup(我的水题之路——世界杯平局数目)
查看>>
POJ 3158 Kickdown(我的水题之路——齿轮盒子,读题失败)
查看>>
POJ 3183 Stump Removal(我的水题之路——高峰烧火,在线判断)
查看>>
POJ 3224 Go for Lab Cup!(我的水题之路——赢的场数最多)
查看>>
POJ 3300 Tour de France(我的水题之路——车轮角速度最大)
查看>>
POJ 3302 Subsequence(我的水题之路——子串判断)
查看>>
POJ 3325 ICPC Score Totalizer Software(我的水题之路——评委评分)
查看>>
POJ 3386 Halloween Hoildays(我的水题之路——两个戒指)
查看>>
POJ 3427 Ecology tax(我的水题之路——不同的理解,不同的AC)
查看>>
POJ 3438 Look and Say(我的水题之路——N个M的队列)
查看>>
SVN服务器的配置
查看>>
Value '0000-00-00' can not be represented as java.sql.Date错误修改
查看>>
配置PHP+mssql环境的一些常见问题及解决方案
查看>>