zoukankan      html  css  js  c++  java
  • Python之协程

    协程操作是单线程进行的,协程通过自拟寄存器记录上下文和栈,实现单线程的高并发

    与多线程相比,协程的特点:

      1.必须在只有一个单线程里实现并发
      2.修改共享数据不需加锁
      3.用户程序里自己保存多个控制流的上下文栈
      4.一个协程遇到I/O操作自动切换到其它协程

    1.通过yield实现简单的生产者消费者模型,拟协程

    # -*- coding:utf-8 -*-
    # Author: Wongdu
    
    import time
    
    # 消费者,通过yield把该函数变成一个生成器
    def consumer(name):
        print("%s 准备好开始吃蛋糕啦~~~" % name)
        while True:
            # 当yield被赋值时,该函数才继续往下执行代码
            dangao = yield
            print("[%s] 被%s吃啦~~~" % (dangao, name))
            time.sleep(1)
    
    # 生产者
    def producer(p_name):
        # 通过生成器的__next__()方法把生成器执行到yield的位置
        c1.__next__()
        c2.__next__()
        n = 0
        while True:
            dangao = '蛋糕%s' % n
            print("33[41;1m[%s] 被[%s]制作出来啦~~33[0m" % (dangao, p_name))
            # 为消费者对象的yield赋值
            c1.send(dangao)
            c2.send(dangao)
            # time.sleep(1)
            n += 1
    
    if __name__ == '__main__':
        c1 = consumer(name='Caiyun')
        c2 = consumer(name='Dudu')
    
        p = producer(p_name='蛋糕贾师傅')

    2.协程之greenlet实现手动I/O切换

    # -*- coding:utf-8 -*-
    # Author:Wong Du
    
    '''
    协程操作是单线程进行的,协程通过自拟寄存器记录上下文和栈,实现单线程的高并发
    1.必须在只有一个单线程里实现并发
    2.修改共享数据不需加锁
    3.用户程序里自己保存多个控制流的上下文栈
    4.一个协程遇到IO操作自动切换到其它协程
    '''
    
    '''
    通过greenlet实现协程间的手动切换
    '''
    from greenlet import greenlet
    
    def run1():
        print(12)
        # 切换到gr2协程执行
        gr2.switch()
        print(34)
        gr2.switch()
    
    def run2():
        print(56)
        # 切换到gr1协程执行
        gr1.switch()
        print(78)
    
    # 注册协程,记录当前协程状态(类似线程中寄存器位置状态记录)
    gr1 = greenlet(run1)
    gr2 = greenlet(run2)
    
    # 切换到gr1协程执行
    gr1.switch()

    3.协程之gevent实现自动I/O切换

    # -*- coding:utf-8 -*-
    # Author:Wong Du
    
    '''
    1.在协程中,通常在遇到I/O读写阻塞时,才进行协程间切换
    2.默认情况下,gevent只能识别gevent下的I/O阻塞,如gevent.sleep(),不能识别time.sleep()阻塞
    3.通过monkey.patch_all()方法,可以识别python中大部分的I/O阻塞,如time.sleep()
    4.下面代码介绍gevent实现自动I/O切换
    '''
    
    import gevent
    import time
    
    # 通过monkey里的patch_all方法,可以识别python中基本全部I/O阻塞
    # from gevent import monkey
    # monkey.patch_all()
    
    # 记录程序开始执行时间
    start_time = time.time()
    
    def func1():
        print("Running in the func1...")
        # 模拟I/O阻塞,睡2秒
        gevent.sleep(2)
        # time.sleep(2)
        print("33[31;1mRunning in the func1 again...33[0m")
    
    def func2():
        print("Running in the func2...")
        # 模拟I/O阻塞,睡1秒
        gevent.sleep(1)
        # time.sleep(1)
        print("33[32;1mRunning in the func2 again...33[0m")
    
    def func3():
        print("Running in the func3...")
        # 模拟I/O阻塞,睡0.5秒
        gevent.sleep(0.5)
        # time.sleep(0.5)
        print("33[33;1mRunning in the func3 again...33[0m")
    
    
    # 通过joinall方法以列表的方式注册多个协程
    gevent.joinall(
        [
            # 注册和自动运行协程,并记录当前协程状态(类似线程中寄存器位置状态记录)
            gevent.spawn(func1),
            gevent.spawn(func2),
            gevent.spawn(func3),
        ]
    )
    
    # 计算程序执行完成花费时间
    print("33[41;1mspeed time:%s33[0m" % (time.time()-start_time))
    '''
    运行结果:
        Running in the func1...
        Running in the func2...
        Running in the func3...
        Running in the func3 again...
        Running in the func2 again...
        Running in the func1 again...
        speed time:2.014172315597534
    '''

    4.两个协程实现简单异步效果实例

      4.1 爬虫之协程gevent

     1 # -*- coding:utf-8 -*-
     2 # Author:Wong Du
     3 
     4 import time, gevent
     5 from urllib.request import urlopen
     6 
     7 # 为当前程序的所有io操作添加gevent可识别的标记,效果类似gevent.sleep()
     8 from gevent import monkey
     9 monkey.patch_all()
    10 
    11 # 爬虫函数
    12 def func(url):
    13     print("Get:", url)
    14     # 打开网页url并接收返回结果
    15     resq = urlopen(url)
    16     # 读取请求返回数据的html页面,以字符串的格式
    17     data = resq.read()
    18     print("%s size: %s" % (url, len(data)))
    19 
    20 # 要爬取的网页url列表
    21 urls = [
    22     'https://www.python.org/',
    23     'https://www.imooc.com/',
    24     'https://www.cnblogs.com/',
    25 ]
    26 
    27 # 计算通过同步执行爬虫程序花费的时间
    28 sync_start_time = time.time()
    29 for url in urls:
    30     func(url)
    31 print("33[31;1msync speed time: %s33[0m" %(time.time()-sync_start_time))
    32 # 结果:sync speed time: 3.6082065105438232
    33 
    34 # 计算通过协程异步执行爬虫程序花费的时间
    35 async_start_time = time.time()
    36 gevent.joinall(
    37     [
    38         gevent.spawn(func, 'https://www.python.org/'),
    39         gevent.spawn(func, 'https://www.imooc.com/'),
    40         gevent.spawn(func, 'https://www.cnblogs.com/'),
    41     ]
    42 )
    43 print("33[32;1masync speed time: %s33[0m" % (time.time()-async_start_time))
    44 # 结果:async speed time: 0.5860333442687988
    同步与异步

      4.2 通过gevent实现一个简单的异步socket连接处理,实现单线程下的socket连接高并发

     1 # -*- coding:utf-8 -*-
     2 # Author:Wong Du
     3 
     4 import socket
     5 HOST = "localhost"
     6 Port = 8001
     7 client = socket.socket()
     8 client.connect((HOST, Port))
     9 
    10 while True:
    11     cmd = input("%s#" % HOST)
    12     if not cmd:
    13         continue
    14     client.send(cmd.encode())
    15     data = client.recv(1024)
    16     print(data.decode())
    gevent_socket_client
     1 # -*- coding:utf-8 -*-
     2 # Author:Wong Du
     3 
     4 import socket, gevent
     5 
     6 # 为当前程序的所有io操作添加gevent可识别的标记,效果类似gevent.sleep()
     7 from gevent import monkey
     8 monkey.patch_all()
     9 
    10 # 建立连接,并注册协程
    11 def server(port):
    12     serve = socket.socket()
    13     serve.bind(("0.0.0.0", port))
    14     serve.listen(3)
    15 
    16     while True:
    17         # 等待连接接入,阻塞,当有连接接入时,往下执行
    18         conn, addr = serve.accept()
    19 
    20         # 注册协程,通过gevent记录并监测该协程的状态(阻塞时切换)
    21         gevent.spawn(handle_request, conn)
    22 
    23 # 连接处理函数
    24 def handle_request(conn):
    25     print(conn)
    26     try:
    27         while True:
    28             res = conn.recv(1024)
    29             print(res.decode())
    30             conn.send(res)
    31             if not res:
    32                 print("client %s is not connected..." % conn.addr)
    33                 conn.shutdown(socket.SHUT_WR)
    34     except Exception as e:
    35         print(e)
    36     finally:
    37         print("---exec done---")
    38         conn.close()
    39 
    40 if __name__ == "__main__":
    41     server(8001)
    gevent_socket_server
  • 相关阅读:
    浮点数越界或者无效1.#IND0
    [转]方差、协方差与相关系数
    『转』 函数、变量命名方法
    感知哈希算法——google用于图片搜索的算法
    C#传值调用与引用调用 解释
    HttpContext.Current.Request.ServerVariab
    怎么去用java经典递归算法?
    泛型的详细解释与示例
    个彻底解释 C#泛型的源代码
    VC++怎么实现Win2000下直接读写磁盘扇区
  • 原文地址:https://www.cnblogs.com/Caiyundo/p/9512877.html
Copyright © 2011-2022 走看看