zoukankan      html  css  js  c++  java
  • 协程/IO多路复用

    一.协程:

    概念:是一个比线程更加轻量级的单位,是组成线程的各个函数

    为什么要有协程:

         想要在单线程内实现并发的效果.但因为Cpython有GIL锁,限制了在同一时间点,CPU只能执行一个线程,所以想要在执行一个线程的期间,充分利用cpu的性能,所以才有了想在单线程内实现并发的效果

    cpu 为什么要切换:

      1.因为某个程序阻塞

      2.因为某个程序用完了时间片

      很明显,解决1 这个问题才能提高效率

    所以想要实现单线程的并发,就要解决在单线程中,多个任务函数中,某个任务函数遇见IO操作,马上自动切换到其他任务函数去执行

    greenlet模块:

    能简单的是实现函数与函数之间的切换,但是遇到IO操作,不能自动切换到其他函数中

    (1) 注册一下函数func,将函数注册成一个对象f1
        f1 = greenlet(func)
    (2) 调用func,使用f1.switch(),如果func需要传参,就在switch里传
    
    from greenlet import greenlet
    import time
    
    def eat(name):
        print(name,'吃炸鸡')
        # time.sleep(1)
        f2.switch('张三')
        print(name,'吃雪糕')
        f2.switch()
    
    def drink(name):
        print(name,'喝啤酒')
        f1.switch()
        print(name,'喝可乐')
    
    f1 = greenlet(eat)
    f2 = greenlet(drink)
    f1.switch('晓雪')
    greenlet

    gevent 模块:

    可以实现在某函数内部遇到IO操作,就自动的切换到其他函数内部去执行

    g = gevent.spawn(func,参数) 注册函数func,返回一个对象g
    gevent.join(g) 等待g指定的func函数执行完毕,如果执行过程中,遇到IO切换
    gevent.joinall([g1,g2,g3]) 等待g1,g2,g3指向的函数func执行完毕
    
    
    import gevent
    import time
    from gevent import monkey
    monkey.patch_all()
    
    def func():
        print(123)
        time.sleep(1)
        print(345)
        # time.sleep(1)
    
    def func1():
        print(456)
        time.sleep(2)
        print(567)
        # time.sleep(1)
    
    g1 = gevent.spawn(func)
    g2 = gevent.spawn(func1)
    g1.join()
    g2.join()

    总结:进程,线程,协程

    协程:是由用户自己去调度的,

    计算密集用多进程,可以充分利用多核CPU的性能,IO密集用多线程

    多线程和协程的区别:

    线程是程序系统调度,控制

    协程是程序员自己调度,控制

    二.IO多路复用:

    1.用非阻塞IO模型解决阻塞IO

    服务端:
    import
    socket sk = socket.socket() sk.setblocking(False) sk.bind(('127.0.0.1',8080)) sk.listen() l = [] del_l = [] while 1: try: conn,addr = sk.accept()# 如果是阻塞IO模型,在这里程序会一直等待。 l.append(conn)# 将每个请求连接的客户端的conn添加到列表中 except BlockingIOError: for conn in l:# 去遍历所有客户端的conn,看看有没有客户端给我发送数据了 try: info = conn.recv(1024).decode('utf-8')# 尝试接收,看看有没有客户端给我发数据 if not info:# 如果客户端正常执行了close,服务器会接收到一个空 del_l.append(conn)# 将已经结束的客户端的conn,添加到要删除的列表中 print('客户端正常退出了!') conn.close()# 因为客户端已经主动close,所以服务器端的conn也要close else: print(info) conn.send(info.upper().encode('utf-8')) except BlockingIOError: continue# 是没有接受到客户端发来的数据而报错 except ConnectionResetError: pass# 是因为客户端强制退出而报错 if del_l: for conn in del_l: l.remove(conn) del_l = []# 在删除完主动关闭的客户端的连接之后,应该把此列表清空,否则报错

    2.基于select的网络IO模型

    服务端:
    import
    select import socket sk = socket.socket() sk.bind(('127.0.0.1',8000)) sk.listen() del_l = [] rlist = [sk]# 是用来让select帮忙监听的 所有 接口 # select:windows/linux是监听事件有没有数据到来 # poll: linux 也可以做select的工作 # epoll: linux 也可以做类似的工作 while 1: r,w,x = select.select(rlist,[],[])# 传参给select,当rlist列表中哪个接口有反应,就返回给r这个列表 # if r: for i in r:# 循环遍历r,看看有反应的接口到底是sk 还是conn if i == sk: # 如果是sk,那就表示有客户端的连接请求 '''sk有数据要接收,代表着有客户端要来连接''' conn,addr = i.accept() #添加if的意思是conn 没有accept sk才有 conn接收数据 所以有if rlist.append(conn)# 把新的客户端的连接,添加到rlist,继续让select帮忙监听 else: # 如果是conn,就表示有客户端给我发数据了 '''conn有数据要接收,代表要使用recv''' try: msg_r = i.recv(1024).decode('utf-8') if not msg_r: '''客户端执行了close,客户端主动正常关闭连接''' del_l.append(i) i.close() else: print(msg_r) i.send(msg_r.upper().encode('utf-8')) except ConnectionResetError: pass if del_l:# 删除那些主动断开连接的客户端的conn for conn in del_l: rlist.remove(conn) del_l.clear()
    import socket
    sk = socket.socket()
    sk.connect(('127.0.0.1',8080))
    
    while 1:
        msg_s = input('>>>')
        if not msg_s:continue
        if msg_s == 'q':break
        sk.send(msg_s.encode('utf-8'))
        print(sk.recv(1024).decode('utf-8'))
    sk.close()
    客户端
  • 相关阅读:
    vim内外部鼠标复制 粘贴
    nginx 问题解决nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
    Installation of the latest version of netease-cloud-music on Fedora 30 linux platform
    Linux就该这么学11学习笔记
    Linux就该这么学10学习笔记
    css day2
    Linux就该这么学09学习笔记
    Linux就该这么学08学习笔记
    css day1
    Linux就该这么学07学习笔记
  • 原文地址:https://www.cnblogs.com/ITdong-1/p/9550693.html
Copyright © 2011-2022 走看看