• 协程


    协程

    进程:资源单位

    线程:执行单位

    协程:单线程下实现并发(能够在多个任务之间切换和保存状态来节省IO)

    多道技术:

      空间上的复用

      时间上的复用

    核心:切换+保存状态

    注意:

      操作系统的切换+保存状态是针对多个线程,

      想在单个线程下自己手动实现操作系统的切换+保存状态的功能,协程产生

      由于协程是程序员自己想出来并实现,它对于操作系统来说根本不存在。操作系统只知道进程和线程。

      需要注意的是:并不是单个线程下实现切换+保存状态就能提升效率,可能没有遇到io也切,那反而会降低效率

        任务是计算密集型,反而会降低效率

        任务是IO密集型,会提升效率

    将单个线程的效率提升到最高,多进程下开多线程,多线程下用协程>>> 实现高并发!!! 

      1)yield能够实现保存上次运行状态,但是无法识别io

    # 串行执行
    import time
    
    
    def func1():
        for i in range(10000000):
            i + 1
    
    
    def func2():
        for i in range(10000000):
            i + 1
    
    
    start = time.time()
    func1()
    func2()
    stop = time.time()
    print(stop - start)
    
    
    
    
    # 基于yield并发执行
    import time
    
    
    def func1():
        while True:
            10000000 + 1
            yield
    
    
    def func2():
        g = func1()
        for i in range(10000000):
            time.sleep(100)  # 模拟IO,yield并不会捕捉到并自动切换
            i + 1
            next(g)
    
    
    start = time.time()
    func2()
    stop = time.time()
    print(stop - start)
    切换➕保存状态

    2)单线程下实现并发

    如果你能够自己通过代码层面监测你自己的io行为
    并且通过代码实现切换+保存状态
    单线程实现高并发
    # ---------------服务端---------------------
    from gevent import monkey; monkey.patch_all()
    from gevent import spawn
    import socket
    
    
    def communicate(conn):
        while True:
            try:
                data = conn.recv(1024)
                if len(data) == 0: break
                print(data.decode('utf-8'))
                conn.send(data.upper())
            except ConnectionResetError:
                break
        conn.close()
    
    
    def server():
        server = socket.socket()
        server.bind(('127.0.0.1', 8080))
        server.listen(5)
        while True:
            conn, addr = server.accept()
            spawn(communicate, conn)
    
    
    if __name__ == '__main__':
        s1 = spawn(server)
        s1.join()
    
    
    
    # ---------------客户端---------------------
    from threading import Thread, current_thread
    import socket
    
    
    def client():
        client = socket.socket()
        client.connect(('127.0.0.1', 8080))
        n = 1
        while True:
            data = '%s %s' % (current_thread().name, n)
            n += 1
            client.send(data.encode('utf-8'))
            info = client.recv(1024)
            print(info)
    
    
    if __name__ == '__main__':
        for i in range(500):
            t = Thread(target=client)
            t.start()

    3)gevent模块的使用

      spawn:帮你管理任务的对象

      gevent模块不能识别它本身以外的所有的IO行为,但是它内部封装了一个模块,能够帮助我们识别所有的IO行为

    from gevent import monkey; 
    monkey.patch_all()  # 监测代码中所有io行为
    from gevent import spawn
    # gevent本身识别不了time.sleep等不属于该模块内的io操作
    import time
    
    
    def heng(name):
        print('%s 哼' % name)
        time.sleep(2)
        print('%s 哼' % name)
    
    
    def ha(name):
        print('%s 哈' % name)
        time.sleep(3)
        print('%s 哈' % name)
    
    
    start = time.time()
    s1 = spawn(heng, 'Tom')
    s2 = spawn(ha, 'Bob')
    
    s1.join()
    s2.join()
    # heng('Tom')
    # ha('Bob')
    
    print('', time.time() - start)

     4)协程实现服务端客户端通信:socket并发

    # 服务端
    from gevent import monkey;monkey.patch_all()
    from socket import *   # !!!后
    from gevent import spawn
    
    
    def communicate(conn):
        while True:
            try:
                data = conn.recv(1024)
                if len(data) == 0: break
                conn.send(data.upper())
            except ConnectionResetError:
                break
        conn.close()
        
    
    def server(ip, port, backlog=5):
        server = socket(AF_INET, SOCK_STREAM)
        server.bind((ip, port))
        server.listen(backlog)
    
        while True:  # 链接循环
            conn, client_addr = server.accept()
            print(client_addr)
    
            # 通信
            spawn(communicate,conn)
    
    
    if __name__ == '__main__':
        g1=spawn(server,'127.0.0.1',8080)
        g1.join()
    
        
    # 客户端
    from threading import Thread, current_thread
    from socket import *
    
    
    def client():
        client = socket(AF_INET, SOCK_STREAM)
        client.connect(('127.0.0.1', 8080))
    
        n = 0
        while True:
            msg = '%s say hello %s' % (current_thread().name, n)
            n += 1
            client.send(msg.encode('utf-8'))
            data = client.recv(1024)
            print(data.decode('utf-8'))
    
    
    if __name__ == '__main__':
        for i in range(500):
            t = Thread(target=client)
            t.start()

    5)IO模型 

    阻塞IO  Blocking I/O
    非阻塞IO
      服务端通信针对accept用s.setblocking(False)加异常捕获,cpu占用率过高
    IO多路复用 multiplexing I/O
      在只检测一个套接字的情况下,他的效率连阻塞IO都比不上。因为select这个中间人增加了环节。
      但是在检测多个套接字的情况下,就能省去wait for data过程
    异步IO  Asynchronous I/O
  • 相关阅读:
    Day6
    Day6
    Day6
    Day6
    Day6
    Day6
    LeetCode "The Skyline Problem"
    HackerRank
    HackerRank
    HackerRank
  • 原文地址:https://www.cnblogs.com/zhouyongv5/p/10840282.html
Copyright © 2020-2023  润新知