Day 42 协程. IO 并发
一、什么是协程?
是单线程下的并发,又称微线程,纤程。英文名Coroutine。一句话说明什么是线程:协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的。
协程相比于线程切换效率更快了.
本质是线程
能够在多个任务之间切换来节省一些IO时间.
协程中任务之间的切换时间开销要远远小于进程线程之间的切换.
真正的协程模块就是使用greenlet完成切换的.
进程和协程的任务切换由操作系统完成.
协程任务之间的切换由程序完成.
协程任务之间的切换由程序代码完成 , 只有遇到协程模块能识别的io操作的时候程序才会进行任务切换,实现并发的效果。
协程:
能够在一个线程中实现并发效果的概念.
能够规避一些任务中的IO操作.
在任务的执行过程中,检测到IO就切换到其他任务.
在两个任务之间来回切换数据(简单的协程的概念)
# import time # def consumer(): # while True: # x = yield # time.sleep(1) # print('处理了数据 :',x) # # def producer(): # c = consumer()#生成一个生成器对象
# next(c) #每次next返回一个值
# for i in range(10):
# time.sleep(1)
# print('生产了数据 :',i)
# c.send(i)
# producer()
输出结果:虽然实现了切换,但是没有节约IO时间.
D:\PycharmProjects\test\venv\Scripts\python.exe D:/parcharm/42.py 生产了数据 : 0 处理了数据 : 0 生产了数据 : 1 处理了数据 : 1 生产了数据 : 2 处理了数据 : 2 生产了数据 : 3 处理了数据 : 3 生产了数据 : 4 处理了数据 : 4 生产了数据 : 5 处理了数据 : 5 生产了数据 : 6 处理了数据 : 6 生产了数据 : 7 处理了数据 : 7 生产了数据 : 8 处理了数据 : 8 生产了数据 : 9 处理了数据 : 9
二 、安装 gevent和greenlet
查看安装状态
真正的协程模块就是使用greenlet完成的切换
from greenlet import greenlet def eat(): print("吃开始") g2.switch() print("吃完了") g2.switch() def play(): print("开始玩") g1.switch() print('玩完了') g1 =greenlet(eat) g2 =greenlet(play) g1.switch()
打印结果:
D:\PycharmProjects\test\venv\Scripts\python.exe D:/parcharm/44.py 吃开始 开始玩 吃完了 玩完了
协程能够在一个线程中实现并发效果的概念。
能够规避一些任务中的IO操作
在任务的执行过程中,检测到IO就切换到其他的任务.
三、爬虫的例子(正则有基础.)
请求过程中的IO等待
import requests #需要pip安装的。 from urllib.request import urlopen#内置的模块 url='http://www.baidu.com' res1 =urlopen(url) res2 = requests.get(url) print(res1) print(res2) print(res1.read().decode('utf-8'))# 有格式的 print(res2.content.decode('utf-8'))#无格式的。
爬虫 (结果是一起显示出来的。)
gevent 用在爬虫和socket的编程中会用的到.
gevent是python的一个并发框架,以微线程greenlet为核心,使用了epoll事件监听机制以及诸多其他优化而变得高效.而且其中有个monkey类,
将现有基于Python线程直接转化为greenlet(类似于打patch).他和线程框架性能比高大概4倍(看下图,是gevent和paste的对比):
from gevent import monkey;monkey.patch_all() import gevent from urllib.request import urlopen def get_url(url): response = urlopen(url) content = response.read().decode('utf-8') return len(content) g1 =gevent.spawn(get_url,'http://www.baidu.com') g2 =gevent.spawn(get_url,'http://www.sohu.com') g3 =gevent.spawn(get_url,'http://www.hao123.com') g4 =gevent.spawn(get_url,'http://www.cnblog.com') g5 =gevent.spawn(get_url,'http://www.cnblog.com') gevent.joinall([g1,g2,g3,g4,g5]) print(g1.value) print(g2.value) print(g3.value) print(g4.value) print(g5.value)
打印结果
D:\PycharmProjects\test\venv\Scripts\python.exe D:/parcharm/44.py 114439 185037 523375 1887 1887
协程实现socket的案例
协程间的切换是代码级别的,
线程间切换时操作系统级别的。
Server端
from gevent import monkey;monkey.patch_all()
import socket
import gevent
def talk(conn):
conn.send(b'hello')
print(conn.recv(1024).decode('utf-8'))
conn.close()
sk = socket.socket()
sk.bind(('127.0.0.1',8080))
sk.listen()
while True:
conn,addr = sk.accept()
gevent.spawn(talk,conn)
sk.close()
client端
import socket sk = socket.socket() sk.connect(('127.0.0.1',8080)) print(sk.recv(1024)) msg = input('>>>').encode('utf-8') sk.send(msg) sk.close()
四 IO模型.
blocking IO 阻塞IO
nonblocking IO 非阻塞IO
IO multiplexing IO 多路复用
asynchronous IO 异步 IO
Blocking IO 阻塞IO
同步:同步提交一个任务之后要等待这个任务执行完毕.
异步只管提交任务,