python greenlet背景介绍与实现机制

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

 并发处理的技术背景

并行化处理目前很受重视, 由于在不少时候,并行计算能大大的提升系统吞吐量,尤为在如今多核多处理器的时代, 因此像lisp这种古老的语言又被人们从新拿了起来, 函数式编程也愈来愈流行。 介绍一个python的并行处理的一个库: greenlet。 python 有一个很是有名的库叫作 stackless ,用来作并发处理, 主要是弄了个叫作tasklet的微线程的东西, 而greenlet 跟stackless的最大区别是, 他很轻量级?不够, 最大的区别是greenlet须要你本身来处理线程切换, 就是说,你须要本身指定如今执行哪一个greenlet再执行哪一个greenlet。程序员

greenlet的实现机制

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

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)安全

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代码.服务器

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)协程有编程语言提供,由程序员控制进行切换,因此没有线程安全问题,能够用来处理状态机,并发请求等。可是没法利用多核优点。
上面的四种方案能够配合使用,我比较看好的是进程+协程的模式。并发

相关文章
相关标签/搜索