• 协程


    一、协程存在的原因

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

      并发 :切换 + 保存状态 (yield 只能单纯的实现状态的保存和切换函数)

                 yield不能实现:当某一个函数中遇到IO阻塞时,自动的切换到另一个函数去执行

                 目标是:当某一个函数中遇到IO阻塞时,程序能自动的切换到另一个函数去执行

                  如果能实现这个功能,那么每个函数都是一个协程。

      CPU是为什么要切换?

        1 、因为某个程序阻塞了

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

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

    二、协程

      1、含义:是一个比线程更加轻量级的单位,是组成线程的各个函数,协程本身没有实体

      2、greenlet模块:能简单的实现函数与函数之间的切换,但是遇到IO操作,不能自动切换到其他函数中。

        (1)注册一下函数func,将函数注册成一个对象f1

          f1 = greenlet(func)

        (2)调用func,使用f1.switch(),如果func需要传参,就在switch这里传即可

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

             gevent不识别别的IO操作,例如:time.sleep(1)就不识别,不会进行函数的切换

             我们可以通过mokey来解决gevent不识别其他IO操作的现象

             mokey.patch_all()  可以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
    
    def func():
        print(111)
        time.sleep(1) # gevent不识别除了自己以外的IO操作,
        gevent.sleep(1) # 执行被阻塞,切换函数去执行别的函数
        print(222)
    
    def func1():
        time.sleep(1)
        print(333)
        gevent.sleep(1)
        print(444)
    
    g1 = gevent.spawn(func) # 实例化一个对象
    g2 = gevent.spawn(func1)
    g2.join()
    g1.join() # 把等待g1的函数执行完毕
    串行和并发的效率对比
    from gevent import monkey
    monkey.patch_all() # gevent可以识别time.sleep()IO操作,是一个阻塞。
    import time
    import gevent
    
    def func(i):
        time.sleep(1)
        print(i)
    
    start = time.time()
    for i in range(10):
        func(i)
    print(time.time() - start)
    
    start1 = time.time()
    l = []
    for i in range(10):
        g = gevent.spawn(func,i)
        l.append(g)
    gevent.joinall(l)
    print(time.time() - start1)

    三、进程、线程、协程区别

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

      IO密集用多线程,(注意:协程是单线程的)

      多线程和协程的区别是 :

        线程是由操作系统调度,控制

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

    四、IO多路复用

      阻塞IO

      非阻塞IO

      多路复用IO

      异步IO  python实现不了,但是有tornado框架,天生自带异步

    五、select   和  poll   和epoll  的区别

      select 和poll有一个共同的机制,都是采用轮询的方式去询问内核,有没有数据准备好了。

      select有一个最大监听事件的限制,32位机限制1024,64位机限制2048

      poll没有,理论上poll可以开启无限大,1G内存大概可以开10W个事件去监听

      epoll是最好的,采用的是回调机制,解决了select和poll共同存在的问题

      而且epoll理论上也可以开启无线多个监听事件

    # 基于select的网络IO模型
    import select
    import socket
    
    sk = socket.socket()
    sk.bind(('127.0.0.1',8080))
    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有数据要接收,代表着有客户端要来连接'''
                    conn,addr = i.accept()
                    rlist.append(conn) # 把新的客户端的连接,添加到rlist,继续让select帮忙监听
                else:
                    try:
                        msg = i.recv(1024).decode('utf-8')
                        if not msg:
                            '''客户端关闭了连接'''
                            del_l.append(i)
                            i.close()
                        else:
                            i.send(msg.upper().encode('utf-8'))
                    except ConnectionResetError:
                        pass
            if del_l: # 删除那些主动断开连接的客户端的conn
                for i in del_l:
                    rlist.remove(i)
                del_l = []

      

  • 相关阅读:
    自制凉皮
    牛人
    史记
    阅读detection
    最近的购书清单
    不要轻易挑战用户的习惯,否则会被用户打脸!
    INVEST原则的应用
    谈谈Backlog梳理活动
    要写封闭式的用户故事
    敏捷教练的八种失败角色
  • 原文地址:https://www.cnblogs.com/wjs521/p/9551425.html
Copyright © 2020-2023  润新知