协程
1、协程(微线程)(纤程)的概念
线程是系统级别的它们由操作系统调度,而协程则是程序级别的由程序根据需要自己调度。在一个线程中会有很多函数,我们把这些函数称为子程序,在子程序执行过程中可以中断去执行别的子程序,而别的子程序也可以中断回来继续执行之前的子程序,这个过程就称为协程。
2、协程的实现方式(3)
(1)yield实现协程效果
def consumer(name):
print('开始吃包子...')
while True:
print('\033[31;1m[consumer]%s需要包子\033[0m'%name)
bone = yield #接收send发送的数据
print('\033[31;1m[%s]吃了%s个包子\033[0m'%(name,bone))
def producer(obj1):
obj1.send(None) #必须先发送None
for i in range(3):
print('\033[32;1m[producer]\033[0m正在做%s个包子'%i)
obj1.send(i
if __name__ == '__main__':
con1 = consumer('消费者A') #创建消费者对象
producer(con1)
(2)greenlet模块实现程序间切换执行
import greenle
def A():
print('a.....')
g2.switch() #切换至B
print('a....2')
g2.switch()
def B():
print('b.....')
g1.switch() #切换至A
print('b....2'
g1 = greenlet.greenlet(A) #启动一个线程
g2 = greenlet.greenlet(B)
g1.switch()
(3)gevent实现协程
import geven
def foo():
print('running in foo')
gevent.sleep(2)
print('com back from bar in to foo')
def bar():
print('running in bar')
gevent.sleep(2)
print('com back from foo in to bar'
gevent.joinall([ #创建线程并行执行程序,碰到IO就切换
gevent.spawn(foo),
gevent.spawn(bar),
])
3、事件驱动
事件驱动编程是一种编程范式,这里程序的执行流由外部事件来决定。它的特点是包含一个事件循环,当外部事件发生时使用回调机制来触发相应的处理,另外两种常用的编程范式是(单线程)同步以及多线程编程。
服务器处理模型的程序时,有以下几种模型:
(1)每收到一个请求,创建一个新的进程,来处理该请求;
(2)每收到一个请求,创建一个新的线程,来处理该请求;
(3)每收到一个请求,放入一个事件列表,让主进程通过非阻塞I/O方式来处理请求
第(1)中方法,由于创建新的进程的开销比较大,所以,会导致服务器性能比较差,但实现比较简单。
第(2)种方式,由于要涉及到线程的同步,有可能会面临死锁等问题。
第(3)种方式,在写应用程序代码时,逻辑比前面两种都复杂。
综合考虑各方面因素,一般普遍认为第(3)种方式是大多数网络服务器采用的方式
4、gevent
1、事件:
import gevent
from gevent.event import Event
evt = Event()
def setter():
print('A: Hey wait for me, I have to do something')
gevent.sleep(3)
print("Ok, I'm done")
evt.set()
def waiter():
print("I'll wait for you")
evt.wait() # blocking
print("It's about time")
def main():
gevent.joinall([
gevent.spawn(setter),
gevent.spawn(waiter),
gevent.spawn(waiter),
])
if __name__ == '__main__': main()
import gevent
from gevent.event import AsyncResult
a = AsyncResult()
def setter():
gevent.sleep(3)
a.set('Hello!')
def waiter():
print(a.get())
gevent.joinall([
gevent.spawn(setter),
gevent.spawn(waiter),
])
Event还有一个扩展AsyncResult(异步),这个扩展可以在set的时候带上数据传递给各waiter去get。这里get还是会阻塞,但是等待的就是不flag了,而是一个值或一个报错。
2、 队列:
队列是一个排序的数据集合,它有常见的put / get操作, 但是它是以在Greenlet之间可以安全操作的方式来实现的。
import gevent
from gevent.queue import Queue
tasks = Queue()
def worker(n):
while not tasks.empty():
task = tasks.get()
print('Worker %s got task %s' % (n, task))
gevent.sleep(0)
print('Quitting time!')
def boss():
for i in xrange(1,25):
tasks.put_nowait(i)
gevent.spawn(boss).join()
gevent.joinall([
gevent.spawn(worker, 'steve'),
gevent.spawn(worker, 'john'),
gevent.spawn(worker, 'nancy'),
])
首先初始化一个Queue()实例。这里会先运行boss() 调用put_nowait()方法不阻塞的往队列里面放24个元素。然后下面依次从Queue里对数字进行消费,起了三个协程分别命名了不同的名字,使用get方法依次消费队列里面的数字直到消费完毕。
put和get操作都有非阻塞的版本,put_nowait和get_nowait不会阻塞,然而在操作不能完成时抛出gevent.queue.Empty或gevent.queue.Full异常。同时Queue队列可以支持设置最大队列值,查看队列现在元素数量qsize(),队列是否为空empty(),队列是否满了full()等。
3、Group/Pool gevent 组合池:
组(group)是一个运行中greenlet的集合,集合中的greenlet像一个组一样 会被共同管理和调度。它也兼饰了像Python的multiprocessing库那样的 平行调度器的角色。就是,在一个组(group)里面的greenlet会被统一管理和调度。
import gevent
from gevent.pool import Group
def talk(msg):
for i in xrange(3):
print(msg)
g1 = gevent.spawn(talk, 'bar')
g2 = gevent.spawn(talk, 'foo')
group = Group()
group.add(g1)
group.add(g2)
group.join()
就是spawn了好几个talk,然后都加到组里面。最后使用group.join()来等待所有spawn完成,每完成一个就会从group里面去掉。
第一个例子Group().map():
rom gevent import getcurrent
from gevent.pool import Group
group = Group()
def hello_from(n):
print('Size of group %s' % len(group))
print('Hello from Greenlet %s' % id(getcurrent()))
return n
x = group.map(hello_from, xrange(3))
print type(x)
print x
这里使用了group.map()这个函数来取得各spawn的返回值。map()是由第二个参数控制迭代次数,并且传递给第一个参数值而运行的。拿这个函数举例,这里会返回一个list构成这个list的对象就是将迭代的参数传进函数运行之后的返回值。这里得到的结果是[0, 1, 2]
第二个例子Group().imap():
import gevent
from gevent.pool import Group
def intensive(n):
gevent.sleep(3 - n)
return 'task', n
print('Ordered')
ogroup = Group()
x = ogroup.imap(intensive, range(3))
print x
for x in ogroup.imap(intensive, xrange(3)):
print x
这里imap与map不一样的地方是map返回list对象,而imap返回一个iterable对象。所以如果要取得里面的值比如想打印就必须写成像代码最后一行那种。(或者直接包一个list让他变成map函数)。另外提一下imap的内部实现,其实是继承了Greenlet对象,在里面启了spawn()。imap里面还有一个挺好用的参数maxsize默认情况是没有设置的当设置了之后,会将迭代变成一批一批的执行,这里再举一个例子:
def intensive(n):
gevent.sleep(2)
return 'task', n
print('Ordered')
ogroup = Group()
x = ogroup.imap(intensive, xrange(20), maxsize=3)
print x
这里运行的时候,会将并行控制到3个,执行也是每2秒执行3个,而不是不设置的时候2秒之后将输出所有的结果。
第三个例子Group().imap_unordered():
import gevent
from gevent.pool import Group
def intensive(n):
gevent.sleep(3 - n)
return 'task', n
igroup = Group()
for i in igroup.imap_unordered(intensive, range(3)):
print(i)
运行了可以看到输出是:
(‘task’, 2)
(‘task’, 1)
(‘task’, 0)
先返回的先回来,这个如果是imap运行的话,会先等上3秒钟开始返回0然后1 2 一次返回。
最后我们再谈一下Pool对象,Group是Pool类的父类。pool是可以指定池子里面最多可以拥有多少greenlet在跑而且申明也很简单:
from gevent.pool import Pool
x = Pool(10)
其他就是继承了一些Group中的用法.