三十二、协程与IO模型

一、协程

进程:启动多个进程 进程之间是由操作系统负责
线程:启动多个线程 真正被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)
View Code

四、基于协程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()

 

 
posted @ 2019-08-15 17:09  凯帅  阅读(305)  评论(0编辑  收藏  举报