留坑
参考:
值得注意的点:
- Python对协程的支持是通过generator实现的。
- Python中,generator的send和throw方法使得generator很像一个协程(coroutine), 但是generator只是一个半协程(semicoroutines),python doc是这样描述的:“All of this makes generator functions quite similar to coroutines; they yield multiple times, they have more than one entry point and their execution can be suspended. The only difference is that a generator function cannot control where should the execution continue after it yields; the control is always transferred to the generator’s caller.
- 一个线程可以多个协程,一个进程也可以单独拥有多个协程,这样python中则能使用多核CPU。(多进程+协程)
- greenlet是真正的协程
- Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程,在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程。
例子1. 用协程实现生产者,消费者模型
"""
1. 用协程实现消费者生产者模型
2. Python对协程的支持是通过generator实现的
3. 有yield的话,就是generator
4. 整个流程无锁,由一个线程执行,produce和consumer协作完成任务,所以称为“协程”,而非线程的抢占式多任务。
"""
def consumer():
r = ''
while True:
# n为send过来的值
# yield类似于断点,有两个作用。
# 1. 生成值
# 2. 在这里断点,交出控制权。切换到另外一个协程
n = yield r
if not n:
return
print('[CONSUMER] Consuming %s...' % n)
r = '200 OK'
def produce(c):
#start generator with None
c.send(None)
n = 0
while n < 5:
n = n + 1
print('[PRODUCER] Producing %s...' % n)
#启动生成器,并附带一个值,r接收yield生成的值
r = c.send(n)
print('[PRODUCER] Consumer return: %s' % r)
c.close()
c = consumer()
produce(c)
例子2. 遇到IO阻塞时自动切换任务,based on gevent,greenlet,monkey
from gevent import monkey; monkey.patch_all()
import gevent
from urllib.request import urlopen
def f(url):
print('GET: %s' % url)
resp = urlopen(url)
data = resp.read()
print('%d bytes received from %s.' % (len(data), url))
gevent.joinall([
gevent.spawn(f, 'https://www.python.org/'),
gevent.spawn(f, 'https://www.yahoo.com/'),
gevent.spawn(f, 'https://www.baidu.com/'),
])
例子3. 单线程下实现多socket并发,based on gevent
server.py
import sys
import socket
import time
import gevent
from gevent import socket,monkey
monkey.patch_all()
def server(port):
s = socket.socket()
s.bind(('0.0.0.0', port))
s.listen(500)
while True:
cli, addr = s.accept()
gevent.spawn(handle_request, cli)
def handle_request(conn):
try:
while True:
data = conn.recv(1024)
print("recv:", data)
conn.send(data + ' [server]'.encode('utf-8'))
if not data:
conn.shutdown(socket.SHUT_WR)
except Exception as ex:
print(ex)
finally:
conn.close()
if __name__ == '__main__':
server(8006)
client.py
import socket
import threading
def sock_conn():
client = socket.socket()
client.connect(("localhost",8006))
count = 0
while True:
#msg = input(">>:").strip()
#if len(msg) == 0:continue
#从客户端收到的数据
client.send( ("hello %s" %count).encode("utf-8"))
#从服务器端收到的数据
data = client.recv(1024)
print("[%s]recv from server:" % threading.get_ident(),data.decode()) #结果
count +=1
client.close()
for i in range(100):
t = threading.Thread(target=sock_conn)
t.start()