• 协程相关


    * 协程(了解)

    进程:资源单位
    线程:执行单位
    协程:这个概念完全是程序员自己意淫出来的 根本不存在
    单线程下实现并发
    我们程序员自己再代码层面上检测我们所有的IO操作
    一旦遇到IO了 我们在代码级别完成切换
    这样给CPU的感觉是你这个程序一直在运行 没有IO
    从而提升程序的运行效率

    多道技术
    切换+保存状态
    CPU两种切换
    1.程序遇到IO
    2.程序长时间占用

    TCP服务端
    accept
    recv

    代码如何做到
    切换+保存状态

    切换
    切换不一定是提升效率 也有可能是降低效率
    IO切 提升
    没有IO切 降低

    保存状态
    保存上一次我执行的状态 下一次来接着上一次的操作继续往后执行
    yield
    计算密集型的任务时协程并不会节省时间,反而会降低程序的执行速度
    # import time
    #
    # # 串行执行计算密集型的任务 1.2372429370880127
    # def func1():
    # for i in range(10000000):
    # i + 1
    #
    # def func2():
    # for i in range(10000000):
    # i + 1
    #
    # start_time = time.time()
    # func1()
    # func2()
    # print(time.time() - start_time)

    # 切换 + yield 2.1247239112854004
    # import time
    #
    #
    # def func1():
    # while True:
    # 10000000 + 1
    # yield
    #
    #
    # def func2():
    # g = func1() # 先初始化出生成器
    # for i in range(10000000):
    # i + 1
    # next(g)
    #
    # start_time = time.time()
    # func2()
    # print(time.time() - start_time)
    对于单线程下,我们不可避免程序中出现io操作,但如果我们能在自己的程序中(即用户程序级别,而非操作系统级别)控制单线程下的多个任务能在一个任务遇到io阻塞时就切换到另外一个任务去计算,
    这样就保证了该线程能够最大限度地处于就绪态,即随时都可以被cpu执行的状态,相当于我们在用户程序级别将自己的io操作最大限度地隐藏起来,从而可以迷惑操作系统,让其看到:该线程好像是一直在计算,
    io比较少,从而更多的将cpu的执行权限分配给我们的线程。
    协程:是单线程下的并发,又称微线程,纤程。英文名Coroutine。一句话说明什么是线程:协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的
    ps:
    1. python的线程属于内核级别的,即由操作系统控制调度(如单线程遇到io或执行时间过长就会被迫交出cpu执行权限,切换其他线程运行) 2. 单线程内开启协程,一旦遇到io,就会从应用程序级别(而非操作系统)控制切换,以此来提升效率(!!!非io操作的切换与效率无关)
    
    

    对比操作系统控制线程的切换,用户在单线程内控制协程的切换

    优点如下:

    1. 协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级
    2. 单线程内就可以实现并发的效果,最大限度地利用cpu

    缺点如下:

    1. 协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启协程
    2. 协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程
    Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程
    安装:
    pip3 install gevent
    遇到IO阻塞时会自动切换任务:
    from gevent import monkey;monkey.patch_all()

    import time
    from gevent import spawn

    """
    gevent模块本身无法检测常见的一些io操作
    在使用的时候需要你额外的导入一句话
    from gevent import monkey
    monkey.patch_all()
    又由于上面的两句话在使用gevent模块的时候是肯定要导入的
    所以还支持简写
    from gevent import monkey;monkey.patch_all()
    """


    def heng():
    print('哼')
    time.sleep(2)
    print('哼')


    def ha():
    print('哈')
    time.sleep(3)
    print('哈')


    def heiheihei():
    print('heiheihei')
    time.sleep(5)
    print('heiheihei')


    start_time = time.time()
    g1 = spawn(heng)
    g2 = spawn(ha)
    g3 = spawn(heiheihei)
    g1.join()
    g2.join() # 等待被检测的任务执行完毕 再往后继续执行
    g3.join()
    # heng()
    # ha()
    # print(time.time() - start_time) # 5.005702018737793
    print(time.time() - start_time) # 3.004199981689453 5.005439043045044

    * 协程实现TCP服务端的并发效果(了解)

    服务端:

    from gevent import monkey;monkey.patch_all()
    import socket
    from gevent import spawn


    def communication(conn):
    while True:
    try:
    data = conn.recv(1024)
    if len(data) == 0: break
    conn.send(data.upper())
    except ConnectionResetError as e:
    print(e)
    break
    conn.close()


    def server(ip, port):
    server = socket.socket()
    server.bind((ip, port))
    server.listen(5)
    while True:
    conn, addr = server.accept()
    spawn(communication, conn)


    if __name__ == '__main__':
    g1 = spawn(server, '127.0.0.1', 8080)
    g1.join()

    客户端:
    from threading import Thread, current_thread
    import socket


    def x_client():
    client = socket.socket()
    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=x_client)
    t.start()

    总结:
    理想状态:
    我们可以通过
    多进程下面开设多线程
    多线程下面再开设协程序
    从而使我们的程序执行效率提升
  • 相关阅读:
    Content-Type 之 application/json 与 text/javascript
    利用 filter 机制 给 静态资源 url 加上时间戳,来防止js和css文件的缓存,利于开发调试
    Tomcat 启动报错:No default web.xml
    $.parseJson 在 firefox 下返回 null 的问题
    利用 spring bean 的属性 init-method 解决因为数据库连接没有初始化而导致首次点击页面超慢的问题
    spring项目的 context root 修改之后,导致 WebApplicationContext 初始化两次的解决方法
    proxool 连接池警告分析:appears to have started a thread named [HouseKeeper] but has failed to stop it
    Log4j 输出的日志中时间比系统时间少了8小时的解决方法,log4j日志文件重复输出
    itext 实现pdf打印数字上标和下标
    log4j 实现只输入我们指定包的日志
  • 原文地址:https://www.cnblogs.com/h1227/p/12790556.html
Copyright © 2020-2023  润新知