python eventlet协程基础

一、协程相关

1.greenlet

Greenlet在import的时候为一对象,即greenlet在程序中为一单例对象。Greenlet主要实现了保存函数的栈顶和栈底、函数在堆中的地址和长度。

Greenlet实现了一个类似于longjump的功能,用于在函数中执行切换。yeild实现了带返回值的longjump。Greenlet的数据结构中记录了父对象的地址,当函数执行switch的时候将返回到父对象中,由父对象对其进行调度。

2.eventlet模型

Greenlet实现了“跳出去”,eventlet增加了hub的概念,实现了“切回来”。

Eventlet将i/o操作、信号等待等阻塞操作都进行了重构。Eventlet将阻塞事件注册到多路复用模型中,当当前线程阻塞的时候会在调度模型中轮询,如果事件就绪则执行对应的协程。因此在编码过程中,默认无阻塞的情况下python将执行当前协程而不会切换。

3.spawn/spawn_n

spawn/spawn_n使用定时器将协程先挂起,在spawn执行后,对应协程并未孵化出来,需要当前协程阻塞让出调度系统才会去处理定时器,在处理定时器的时候将对应的协程孵化出来。

4.openstack api启动流程

        wsgi.set_eventlet_hub()
        eventlet.hubs.get_hub().clock = monotonic.monotonic

        """

        openstack api.conf

        """

        server = wsgi.Server()
        server.start(config.load_paste_app('app-name'), default_port=app-port)
        """

        ...

        """

        server.wait()

wsgi.set_eventlet_hub:将hub从epoll改为poll。

eventlet.hubs.get_hub().clock = monotonic.monotonic:将定时器从系统时间改为晶振时间。

server.start:创建socket、注册信号处理函数(worker == 0不需要)、拉子进程

       主进程:注销socket,server.wait等待子进程结束

       子进程:起线程池,执行eventlet.wsgi.server

5.eventlet.wsgi

该函数为一死循环(可将is_accepting置为false结束),循环执行socket.accept,当有请求时建立连接,并将连接作为参数孵化一个router协程,当accept阻塞的时候调度到协程中。

 

二、python对象模型

1.基础python对象

在python中,所有变量、函数、类都是对象Py_Object,pyobject共有的数据结构是

{Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;  }

ob_refcnt为该对象被引用的次数,为0时执行对应的析构;ob_type为该对象的类型(即type),type本身也是一个pyobject。。。

 

2.可变变量和不可变变量

python中所有的=赋值都是传递的指针,因此可以认为python的函数都是实参传递,区别在于传递的参数是可变还是不可变。

不可变变量主要为数值和字符串。python在堆中维护对应的列表,同值的不可变变量在内存指向同一个地址,例如

a = 1

a += 1

 当a = 1时,a指向内存中int(1)的地址,当执行a += 1时,实际执行的是a.__add__(self, 1),返回的是int(2)的地址。即不可变变量在值修改的时候并没有修改对应对象的值,而是新生成一个新值的对象返回。

而不可变量的地址则不会变化,对不可变变量进行修改则会真实修改内存中的值。

 

3.python运行时堆栈的变化

以最简单的加法为例

a = 250

b = 250

def add(a, b):

    c = a + b

    return c

在c语言中调用该函数时,栈中保存a、b、c、和调用前的bp值。当调用结束时c的值作为返回值传递给ax,再把bp值pop出来。这样的函数在编译的时候就已经能够确定其占用的栈大小。

python和传统的静态语言不一样的是,python的每一个对象都可以拓展内部的值。例如已经实例化的一个对象host_controller = Controller(req, host_meta),可以在里面增加属性host_controller.test = {"name": "test"},之前也已经讲到,python的每一个变量都是指向栈中具体对象的指针,因此Python的栈中都是*Py_Object的指针,而其具体指在heap中。

当执行add的时候,会在内存中留出a、b和c的*Py_Object的地址,即python函数在执行的时候栈中只保存了对应的指针,实际的对象内容在堆中。

4.字符串对象

python中,字符串对象也是不可变,内容相同的字符串指向内存中同一地址(unicode对象没研究过,应该实现机制类似)。string对象除了维护char类型的数组外,还有hash值,因此在编码时尽量用host_id或者host_name作为索引,因为这两个值在程序中大部分时候都存在在内存中,hash值不需要重复计算,直接以此为索引可以增加命中速度。

三、GIL

前面提到,python的对象都在堆中,因而不像静态语言一样函数调用结束的时候直接移动bp和sp指针即可释放内存。前面又提有每个python对象都有ob_refcnt值,这个值代表了对象的引用次数,每当赋值给一个变量的时候ob_refcnt会自加1,当变量被释放或被赋予其他对象指针时,ob_refcnt会自减1。当ob_refcnt为0时python会执行该对象的析构函数__del__,然后free掉对应内存。而且python对象都保存在堆中,多线程都可以读写,无法保证ob_refcnt变化和析构之间的原子性,因此对线程加锁。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值