协程本质上仍然是一个线程,其能够在多个任务之间切换来节省一些 IO 时间。
def consumer():
while True:
x = yield
print("处理了数据")
def producer():
c = consumer()
next(c)
for i in range(10):
print("生产了数据:", i)
c.send(i)
producer()
# 一边生产一边消耗,它并不能节省时间
greenlet
模块来支持
from greenlet import greenlet
def eat():
print('eating start')
g2.switch()
print('eating end')
def play():
print('playing start')
g1.switch()
print('playing end')
g2.switch()
g1 = greenlet(eat)
g2 = greenlet(play)
g1.switch()
协程一般是为了规避 IO 时间的,对于计算型的,它并不能减少时间。一个线程最多可以起 500 个协程。
gevent
是一个支持协程的模块。gevent
的优势在于:用同步的方法来写异步的代码。一句话来阐述:使用 IO 多路复用对文件描述符的事件进行监听,从而使协程透明切换。
from gevent import monkey
monkey.patch_all() <<--- 注意这个猴子补丁,它捕获程序中的 IO 时间,遇到会自动切换
import time
import gevent
def eat():
print('eating start')
time.sleep(1)
print('eating end')
def play():
print('playing start')
time.sleep(1)
print('playing end')
g1 = gevent.spawn(eat)
g2 = gevent.spawn(play)
g1.join()
g2.join()
# 可以查看它的线程名,发现它的名字是 DummyThread-1
# g.join(g_lst)
一般用在爬虫中。协程实现一个 socket 服务端
from gevent import monkey
monkey.patch_all()
import socket
import gevent
def talk(conn):
conn.send(b'hellow')
print(conn.recv(1024).decode('utf-8'))
conn.close()
sk = socket.socket()
sk.bind(('127.0.0.1', 8000))
sk.listen()
while True:
conn, addr = sk.accept()
gevent.spawn(talk, conn)
gevent 提供了事件,感觉与线程的那个事件差不多:
import gevent
from gevent import Event
evt = Event()
def setter():
print('wait for me')
gevent.sleep(3)
print('done')
evt.set()
def waiter():
print('wait...')
evt.wait()
print('finist')
gevent.joinall(
[
gevent.spawn(setter),
gevent.spawn(waiter),
gevent.spawn(waiter),
gevent.spawn(waiter),
]
)
结果:
wait for me
wait...
wait...
wait...
done
finist
finist
finist
还有一个 AsyncResult 事件,用于在唤醒时传递消息。
from gevent.event import AsyncResult
aevt = AsyncResult()
def setter():
print('wait ')
gevent.sleep(3) # 3秒后唤醒所有在evt上等待的协程
print('done')
aevt.set('hello') # 唤醒,并传递消息
def waiter():
print("I‘ll wait for you")
message = aevt.get() # 等待,并在唤醒时获取消息
print("Got wake up message: %s"% message)
gevent.joinall(
[
gevent.spawn(setter),
gevent.spawn(waiter),
gevent.spawn(waiter),
gevent.spawn(waiter),
]
)
结果:
wait
I‘ll wait for you
I‘ll wait for you
I‘ll wait for you
done
Got wake up message: hello
Got wake up message: hello
Got wake up message: hello
gevent 提供了一个队列对象,可以在协程之间安全的访问
import gevent
from gevent.queue import Queue
products = Queue()
def consumer(name):
while not products.empty():
print("%s got product % s"% (name, products.get()))
gevent.sleep(0)
print("%s quit"% name)
def producer():
for i in range(1, 10):
products.put(i)
gevent.joinall([
gevent.spawn(producer),
gevent.spawn(consumer, 'steve'),
gevent.spawn(consumer, 'john'),
gevent.spawn(consumer, 'nancy'),
])
结果:
steve got product 1
john got product 2
nancy got product 3
steve got product 4
john got product 5
nancy got product 6
steve got product 7
john got product 8
nancy got product 9
steve quit
john quit
nancy quit
还有一个 BoundedSemaphore 计数信号量:
import gevent
from gevent.lock import BoundedSemaphore
sem = BoundedSemaphore(2)
def worker(n):
sem.acquire()
print('Worker %i acquired semaphore' % n)
gevent.sleep(2)
sem.release()
print('Worker %i released semaphore' % n)
gevent.joinall([gevent.spawn(worker, i) for i in range(0, 6)])
结果:
Worker 0 acquired semaphore
Worker 1 acquired semaphore
Worker 0 released semaphore
Worker 1 released semaphore
Worker 2 acquired semaphore
Worker 3 acquired semaphore
Worker 2 released semaphore
Worker 3 released semaphore
Worker 4 acquired semaphore
Worker 5 acquired semaphore
Worker 4 released semaphore
Worker 5 released semaphore
协程的本地变量:
import gevent
from gevent.local import local
data = local()
def f1():
data.x = 1
print(data.x)
def f2():
try:
print(data.x)
except AttributeError:
print('x is not visible')
gevent.joinall([
gevent.spawn(f1),
gevent.spawn(f2)
])
结果:
1
x is not visible
本地变量,当一个协程在内部使用了 data
对象后,那么它就在其它协程内不可见了。
网友评论