python3协程实操备忘
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
'''
协程:又称微线程,纤程,只有一个线程。但由程序根据需要自己调度,协同运行!
1.生成器yield实现协程功能:yield控制生成器执行的中断,并记录执行的位置;next()函数和send()方法控制程序继续执行
2.greenlet模块实现程序间切换执行:
gevent安装需要依赖greenlet,以下为gevent的安装方法,同时会安装greenlet
pip3 install --user gevent
先创建greenlet对象,再使用switch()方法进行跳转
3.gevent模块实现程序间切换执行:----推荐使用,特别是爬虫
第三方库,gevent会主动识别程序内部的IO操作,当子程序遇到IO后,切换到别的子程序。如果所有的子程序都进入IO,则阻塞。
gevent.joinall([gevent.spawn(f,args),gevent.spawn(f,args),.....]),创建线程并行执行程序,碰到IO就切换
显著提高程序运行速度
from gevent import monkey
monkey.patch_all() #将程序中所有IO操作做上标记使程序非阻塞状态,速度更快
'''
#************gevent爬虫异步IO阻塞切换:实现协程功能**************
import gevent
from gevent import monkey
monkey.patch_all() #将程序中所有IO操作做上标记使程序非阻塞状态,速度更快!
from urllib.request import urlopen
import random
import os
def func(url):
print("获得url:%s"%url)
resp = urlopen(url) # 对象 http.client.HTTPResponse
# print(type(resp))
data = resp.read() # bytes类型的数据
# print(type(data))
filename = ""
for i in range(10): # 获得一个由字母和数据随机生成的10位数
a = str(random.choice([random.randrange(10),chr(random.randrange(65,91))]))
filename += a
filename = filename + ".html" # 把10位的随机数作为文件名
filename = os.path.join(os.path.dirname(os.path.abspath(__file__)),"html",filename) # 获得是保存文件的绝对路径
with open(filename,"wb") as f:
f.write(data)
if __name__ == "__main__":
gevent.joinall([
gevent.spawn(func, "http://www.mmjpg.com/"), #创建线程并行执行程序,碰到IO就切换
gevent.spawn(func, "http://www.xiaohuar.com/"),
gevent.spawn(func, "http://www.zol.com.cn"),
])
'''
#************greenlet实现协程功能**************
from greenlet import greenlet
def A(n):
print("A....1.....%s"%n)
b.switch() # 启动greenlet对象b,并运行
print("A.....2.。。。。%s"%n)
b.switch()
def B():
print("B.....3")
a.switch() # 启动greenlet对象a(函数内无法再传参数)
print("B......4")
if __name__ == "__main__":
a = greenlet(A) # 获得一个greenlet对象,但没有启动,<greenlet.greenlet object at 0x7f6a98756cc0>
b = greenlet(B) # 相当于把函数变成了greenlet对象
a.switch(5) # 启动a函数,并传参数给到函数---
'''
'''
#************生成器yield实现协程功能**************
import time
def producer(name):
print("%s我要开始做包子了。。。"%name)
next(conn1) # 启动消费者生成器。碰到yield停止,第一次启动必须用next方法。或者是send(None)
next(conn2)
i = 0
j = 1
while True:
i += 2
j += 2
print("%s生产了一个包子%s"%(name,i))
print("%s生产了一个包子%s"%(name,j))
conn1.send(i) # 继续启动消费者生成器。继续执行。碰到yield停止。并把包子名称发送给到yield前面的变量接收
conn2.send(j)
time.sleep(2)
def consumer(name):
print("%s我要开始吃包子了。。。"%name)
while True:
bz_name = yield
print("%s正吃包子%s。。。"%(name,bz_name))
if __name__ == "__main__":
conn1 = consumer("c1") # 创建一个生产器对象
conn2 = consumer("c2")
producer("scz") # 调用生产者函数。启动生产包子
'''