文章目录
- 1. 进程、线程、协程区分
- 1.1 进程和协程
- 1.2 线程和协程
- 2. 简介
- 2.1 greenlets
- 3. 特点
- 4. 安装
- 5. 示例
- 5.1 用greenlet执行一个函数
- 5.2 创建协程任务
- 5.3 同步和异步执行
- 5.4 同步vs异步
1. 进程、线程、协程区分
我们通常所说的协程Coroutine其实是corporate routine的缩写,直接翻译为协同的例程,一般我们都简称为协程。
在linux系统中,线程就是轻量级的进程,而我们通常也把协程称为轻量级的线程即微线程。
1.1 进程和协程
下面对比一下进程和协程的相同点和不同点:
相同点:
而寄存器和栈的结合就可以理解为上下文,上下文切换的理解:
CPU看上去像是在并发的执行多个进程,这是通过处理器在进程之间切换来实现的,操作系统实现这种交错执行的机制称为上下文切换
操作系统保持跟踪进程运行所需的所有状态信息。这种状态,就是上下文。
在任何一个时刻,操作系统都只能执行一个进程代码,当操作系统决定把控制权从当前进程转移到某个新进程时,就会进行上下文切换,即保存当前进程的上下文,恢复新进程的上下文,然后将控制权传递到新进程,新进程就会从它上次停止的地方开始。
不同点:
1.2 线程和协程
既然我们上面也说了,协程也被称为微线程,下面对比一下协程和线程:
协程只是在单一的线程里不同的协程之间切换,其实和线程很像,线程是在一个进程下,不同的线程之间做切换,这也可能是协程称为微线程的原因吧。
2. 简介
Gevent模块是一种基于协程的Python网络库,它用到Greenlet提供的,封装了libevent事件循环的高层同步API。它让开发者在不改变编程习惯的同时,用同步的方式写异步I/O的代码。
2.1 greenlets
如果想了解gevent的调度流程,最重要的是对greenlet有基本的了解。下面总结一些个人认为比较重要的点:
在gevent中,有两个类继承了greenlet.greenlet,分别是gevent.hub.Hub和gevent.greenlet.Greenlet。后文中,如果是greenlet.greenlet这种写法,那么指的是原生的类库greentlet,如果是greenlet(或者Greenlet)那么指gevent封装后的greenlet。
gevent中的主要模式, 它是以C扩展模块形式接入Python的轻量级协程。 全部运行在主程序操作系统进程的内部,但它们被程序员协作式地调度。
在任何时刻,只有一个协程在运行。
区别于multiprocessing、threading等提供真正并行构造的库, 这些库轮转使用操作系统调度的进程和线程,是真正的并行。
3. 特点
基于libev的快速事件循环(Linux上epoll,FreeBSD上kqueue)。基于greenlet的轻量级执行单元。
API的概念和Python标准库一致(如事件,队列)。
可以配合socket,ssl模块使用。
能够使用标准库和第三方模块创建标准的阻塞套接字(gevent.monkey)。
默认通过线程池进行DNS查询,也可通过c-are(通过GEVENT_RESOLVER=ares环境变量开启)。
TCP/UDP/HTTP服务器
子进程支持(通过gevent.subprocess)
线程池
4. 安装
安装和依赖
依赖于greenlet library
支持python 2.6+ 、3.3+
5. 示例
5.1 用greenlet执行一个函数
#coding:utf-8import time
from greenlet import greenlet
def eat():
print('魔降风云变 is eating')
time.sleep(0.5)
print('魔降风云变 finished eat')
def sleep():
print('小马过河 is sleeping')
time.sleep(0.5)
print('小马过河 finished sleep')
g1 = greenlet(eat)
g1.switch()
--------------结果:
魔降风云变 is eating魔降风云变 finished eat
在一个任务函数中切换到另一个任务函数去执行,然后没有再切换回来
#coding:utf-8import time
from greenlet import greenlet
def eat():
print('魔降风云变 is eating')
g2.switch()
time.sleep(0.5)
print('魔降风云变 finished eat')
def sleep():
print('小马过河 is sleeping')
time.sleep(0.5)
print('小马过河 finished sleep')
g1 = greenlet(eat)
g2 = greenlet(sleep)
g1.switch()
--------------结果:
魔降风云变 is eating小马过河 is sleeping
小马过河 finished sleep
一个任务中切换到另一个任务,另一个任务执行完了再执行切换回到这个任务执行。实现两个任务间切换并都执行结束
#coding:utf-8import time
from greenlet import greenlet
def eat():
print('魔降风云变 is eating')
g2.switch()
time.sleep(0.5)
print('魔降风云变 finished eat')
def sleep():
print('小马过河 is sleeping')
time.sleep(0.5)
print('小马过河 finished sleep')
g1.switch()
g1 = greenlet(eat)
g2 = greenlet(sleep)
g1.switch()
------------结果:
魔降风云变 is eating小马过河 is sleeping
小马过河 finished sleep
魔降风云变 finished eat
5.2 创建协程任务
``python#coding:utf-8
import time
import gevent
def eat():
print('魔降风云变 is eating')
time.sleep(1)
print('魔降风云变 finished eat')
def sleep():
print('小马过河 is sleeping')
time.sleep(1)
print('小马过河 finished sleep')
g1 = gevent.spawn(eat) # 创造一个协程任务
-----------结果:没有输出
协程遇到阻塞才切换,这里代码从上到下执行结束,没有遇到阻塞
import gevent
def eat():
print('魔降风云变 is eating')
time.sleep(1)
print('魔降风云变 finished eat')
def sleep():
print('小马过河 is sleeping')
time.sleep(1)
print('小马过河 finished sleep')
g1 = gevent.spawn(eat) # 创造一个协程任务
gevent.sleep(2) #加个gevent.sleep(2)就切换到eat执行里面的代码了
---------结果:
魔降风云变 is eating魔降风云变 finished eat
#加个gevent.sleep(2)就切换到eat执行里面的代码了。eat中间time.sleep(1)照样睡。
5.3 同步和异步执行
两个子任务之间的 切换也就是上下文切换。
创建Greenlets,gevent对Greenlet初始化提供了一些封装.
第一个
def test1():
print 12
gevent.sleep(0)
print 34
def test2():
print 56
gevent.sleep(0)
print 78
gevent.joinall([
gevent.spawn(test1),
gevent.spawn(test2),
])$ python gevent2.py
12
56
34
78
第二个
import geventfrom gevent import Greenlet
def foo(message, n):
gevent.sleep(n)
print(message)
thread1 = Greenlet.spawn(foo, "Hello", 1)
thread2 = gevent.spawn(foo, "I live!", 2)
thread3 = gevent.spawn(lambda x: (x+1), 2)
threads = [thread1, thread2, thread3]
gevent.joinall(threads)
第二个
$ python gevent3.pyHello
I live!
5.4 同步vs异步
import geventimport random
def task(pid):
gevent.sleep(random.randint(0,2)*0.001)
print('Task %s done' % pid)
def synchronous():
for i in xrange(5):
task(i)
def asynchronous():
threads = [gevent.spawn(task, i) for i in xrange(5)]
gevent.joinall(threads)
print('Synchronous:')
synchronous()
print('Asynchronous:')
asynchronous()$ python gevent3.py
Synchronous:
Task 0 done
Task 1 done
Task 2 done
Task 3 done
Task 4 done
Asynchronous:
Task 2 done
Task 0 done
Task 1 done
Task 3 done
Task 4 done
参考连接:
https://softlns.github.io/2015/11/28/python-gevent/
http://codingdict.com/blog/article/2019/7/11/3560.html