一、协程
进程:启动多个进程 进程之间是由操作系统负责
线程:启动多个线程 真正被cpu执行的最小单位实际是线程
开启一个线程 创建一个线程 需要寄存器 堆栈
协程:本质上是一个线程,能在多个任务之间切换来节省一些IO时间
协程中任务之间的切换也消耗时间,但是开销远远小于进程线程之间的切换
都是实现并发的手段
真正的协程模块就是使用greenlet模块完成的切换
rom greenlet import greenlet def eat(): print("eating start") g2.switch() print("eating end") g2.switch() def play(): print("playing start") g1.switch() print("playing end") g1 = greenlet(eat) g2 = greenlet(play) g1.switch()
二、gevent模块
"""一般最大限度可以开:
进程 线程 协程
5 20 500 最多可以实现5w个并发"""
注意gevent模块没办法自动识别time.sleep等io情况
需要你手动再配置一个参数
from gevent import monkey;monkey.patch_all() # 识别所有IO操作,在导入gevent模块之前 import gevent import time import threading def eat(): print(threading.current_thread().getName()) #协程其实就是假的线程 print("eating start") time.sleep(1) print("eating end") def play(): print(threading.current_thread().getName()) print("playing start") time.sleep(1) print("playing end") g1 = gevent.spawn(eat) #(检测是否有阻塞) g2 = gevent.spawn(play) g1.join() g2.join() # 进程和线程的任务切换由操作系统完成 # 协程任务之间的切换由程序(代码)完成,只要遇到协程模块能识别的IO操作的时候,程序才会进行任务切换,实现并发效果
from gevent import monkey;monkey.patch_all() import time import gevent def task(): time.sleep(1) print(123456) def sync(): for i in range(10): task() def async(): g_lis = [] for i in range(20): g = gevent.spawn(task) g_lis.append(g) # for g in g_lis:g.join() gevent.joinall(g_lis) # 接收可迭代对象 sync() async() 协程在运行过程中,先集体等待一秒,然后一次全部打印出来"""
三、协程应用
小爬虫:
from gevent import monkey; monkey.patch_all() import requests import gevent def get_url(url): res = requests.get(url) print(res) content = res.content.decode("utf8") # print(content) return len(content) g1 = gevent.spawn(get_url, "http://www.baidu.com") g2 = gevent.spawn(get_url, "http://www.sogou.com") g3 = gevent.spawn(get_url, "http://www.taobao.com") g4 = gevent.spawn(get_url, "http://www.hao123.com") g5 = gevent.spawn(get_url, "http://www.cnblogs.com") gevent.joinall([g1, g2, g3, g4, g5]) print(g1.value) print(g2.value) print(g3.value) print(g4.value) print(g5.value)
四、基于协程socket高并发通信
#服务端 import socket from gevent import monkey;monkey.patch_all() import gevent server = socket.socket() server.bind(("127.0.0.1", 8080)) server.listen(5) def chat(conn): conn.send(b"hello") print(conn.recv(1024).decode("utf8")) conn.close() while True: conn, addr = server.accept() g1 = gevent.spawn(chat, conn) server.close()
客户端 import socket client = socket.socket() client.connect(("127.0.0.1", 8080)) msg = client.recv(1024) print(msg) ret = input(">>>>>>>>:").encode("utf8") client.send(ret) client.close()
五、IO模型
五种 IO Model:
blocking IO 阻塞IO
nonblocking IO 非阻塞IO
IO multiplexing IO多路复用
signal driven IO 信号驱动IO
asynchronous IO 异步IO
当一个read操作发生时:1.等待数据准备 2.将数据从内核(kernel)拷贝到进程中
同步:提交一个任务之后等待这个任务执行完毕
异步:只管提交任务,不等待这个任务执行完毕就可以做其他事情
阻塞:recv(等待接收字节数据copy) recvfrom(接收) accept(等待别人给你打电话)
阻塞: 线程 运行状态 》》》》阻塞状态》》》》》》》》就绪
非阻塞:在非阻塞IO中,用户进程其实是要不断的主动询问操作系统数据准备好了没
"""
非阻塞IO模型:
#服务端 import socket server = socket.socket() # 拿到手机 server.bind(("127.0.0.1", 8080)) # 绑定IP和端口 server.listen() server.setblocking(False) # 变成非阻塞模式 conn_l = [] # 用来存储所有来请求server端的conn连接 del_conn = [] # 用来存储所有已经断开与server端连接的conn while True: try: conn, addr = server.accept() # 非阻塞,但是没人连接会报错 print("建立连接了") conn_l.append(conn) except BlockingIOError: for con in conn_l: try: msg = con.recv(1024) # 非阻塞 没人发消息会报错 if msg == b"": del_conn.append(con) continue print(msg) con.send(b"byebye") except BlockingIOError: pass for con in del_conn: conn_l.remove(con) del_conn.clear()
#客户端 import socket import time client = socket.socket() client.connect(("127.0.0.1", 8080)) while True: client.send(b"hello") client.close()