zoukankan      html  css  js  c++  java
  • 4.5 协程

    协程

    相关概念

    目的

    单线程下实现并发

    概念特性

     

    纤程,微线程
    非抢占多任务子程序的计算机组件
    允许不同入口点的暂停和开始
    简单来说: 协程是可以暂停执行的函数

    内部原理

    记录一个函数的上下文栈帧(空间),
    协程调度切换时会将记录的上下文保存,在切换回来时候进行调取,
    恢复原有的执行内容,以便从上一次执行的位置继续执行。    

    优缺点

    优点:
            切换在应用层完成,开销小
            单线程程序,不需要进行共享资源的互斥处理
    
    缺点:
            无法利用计算机多核资源

    yield 

    yield send 实例

    import time
    def wrapper(func):
        def inner(*args,**kwargs):
            ret =func(*args,**kwargs)
            next(ret)
            return ret
        return inner
    @wrapper
    def consumer():
        while True:
            x= yield
            print(x)
    
    def producter(target):
        '''生产者造值'''
        # next(g)  #相当于g.send(None)
        for i in range(10):
            time.sleep(0.5)
            target.send(i)#要用send就得用两个yield
    producter(consumer())
    View Code

    Greenlet

    概念

    安装

    pip3 install greenlet

    用法

    实例

    from greenlet import greenlet
    import time
    def eat(name):
        print('%s eat 1' %name)
        time.sleep(10)  #当遇到IO的时候它也没有切,这就得用gevent了
        g2.switch('egon')
        print('%s eat 2' %name)
        g2.switch()
    def play(name):
        print('%s play 1' %name)
        g1.switch()
        print('%s play 2' %name)
    
    g1=greenlet(eat)
    g2=greenlet(play)
    
    g1.switch('egon')#可以在第一次switch时传入参数,以后都不需要

    Gevent

    概念

      对程序内部协作式地调度,轻松实现并发同步或异步编程

      自动识别 阻塞 ,然后自行切换任务。不在需要手动

    安装

    pip3 install gevent

     相关方法

    g1=gevent.spawn(func,1,2,3,x=4,y=5)  # 创建一个协程对象g1,spawn括号内第一个参数是函数名,后面可以多个参数,可以是位置实参或关键字实参,都是传给函数eat的
    g=gevent.spawn(func2)
    
    g1.join() #等待g1结束
    g2.join() #等待g2结束
    #或者上述两步合作一步:gevent.joinall([g1,g2])
    
    gevent.sleep(2)  # 模拟的是gevent可以识别的io阻塞
    g1.value  #拿到func1的返回值
    
    
    # 当不使用 gevent.sleep(2),使用 time.sleep() 得时候需要导入这个
    from gevent import monkey;monkey.patch_all() # 如果不导入直接使用 time.sleep() 会无法实现单线程并发
    # 必须放到被打补丁者的前面,如time,socket模块之前

    实例

    基本使用实例

    from gevent import monkey;monkey.patch_all()
    import gevent
    import time
    def eat(name):
        print('%s eat 1' %name)
        time.sleep(2)
        print('%s eat 2' %name)
        return 'eat'
    def play(name):
        print('%s play 1' %name)
        time.sleep(3)
        print('%s play 2' %name)
        return 'play'  #当有返回值的时候,gevent模块也提供了返回结果的操作
    
    start = time.time()
    g1 = gevent.spawn(eat,'egon')  #执行任务
    g2 = gevent.spawn(play,'egon')  #g1和g2的参数可以不一样
    # g1.join()  #等待g1
    # g2.join()  #等待g2
    #上面等待的两句也可以这样写
    gevent.joinall([g1,g2])
    print('',time.time()-start) #3.001171588897705
    
    print(g1.value)
    print(g2.value)
    
    """
    egon eat 1
    egon play 1
    egon eat 2
    egon play 2
    主 3.019590377807617
    eat
    play
    """
    View Code

    爬虫实例

    from gevent import monkey;monkey.patch_all()  #打补丁
    import gevent
    import requests
    import time
    def get_page(url):
        print('get :%s'%url)
        response = requests.get(url)
        if response.status_code==200: #下载成功的状态
            print('%d bytes received from:%s'%(len(response.text),url))
    start=time.time()
    gevent.joinall([
        gevent.spawn(get_page,'http://www.baidu.com'),
        gevent.spawn(get_page, 'https://www.yahoo.com/'),
        gevent.spawn(get_page, 'https://github.com/'),
    ])
    stop = time.time()
    print('run time is %s' %(stop-start))
    View Code

    待回调函数得爬虫实例

    from gevent import joinall, spawn, monkey
    
    monkey.patch_all()
    import requests
    from threading import current_thread
    
    
    def parse_page(res):
        print('%s PARSE %s' % (current_thread().getName(), len(res)))
    
    
    def get_page(url, callback=parse_page):
        print('%s GET %s' % (current_thread().getName(), url))
        response = requests.get(url)
        if response.status_code == 200:
            callback(response.text)
    
    
    if __name__ == '__main__':
        urls = [
            'https://www.baidu.com',
            'https://www.taobao.com',
            'https://www.openstack.org',
        ]
    
        tasks = []
        for url in urls:
            tasks.append(spawn(get_page, url))
    
        joinall(tasks)
    
    """
    DummyThread-1 GET https://www.baidu.com
    DummyThread-2 GET https://www.taobao.com
    DummyThread-3 GET https://www.openstack.org
    DummyThread-1 PARSE 2443
    DummyThread-2 PARSE 141762
    DummyThread-3 PARSE 65694
    """
    View Code

    gevent 实现并发 socket 

    import gevent
    from gevent import monkey
    
    monkey.patch_all()
    
    from socket import *
    
    
    def server():
        s = socket()
        s.bind(("127.0.0.1", 8090))
        s.listen()
        while True:
            c, addr = s.accept()  # 要在这里阻塞一下等待连接
            print("Connect from", addr)
            # handle(c)  # 处理客户端请求
            gevent.spawn(handle,c) # 利用协程来实现高并发
    
    def handle(c):
        while True:
            data = c.recv(1024)
            if not data:
                break
            print(data.decode())
            c.send(b'ok')
        c.close()
    
    
    server()
    tcp_server
    import socket
    sk = socket.socket()  # 创建客户套接字
    sk.connect(('127.0.0.1',8090))  # 尝试连接服务器
    sk.send(b'hello!')
    ret = sk.recv(1024)  # 对话(发送/接收)
    print(ret)
    sk.close()  # 关闭客户套接字
    tcp_client
  • 相关阅读:
    团体程序设计天梯赛 L2-011 玩转二叉树 (25分)
    团体程序设计天梯赛 L2-010 排座位 (25分)(并查集)
    团体程序设计天梯赛 L2-009 抢红包 (25分)
    团体程序设计天梯赛 L2-007 家庭房产 (25分)
    团体程序设计天梯赛 L2-014 列车调度 (25分)(最长上升子序列)
    团体程序设计天梯赛 L2-006 树的遍历 (25分)
    团体程序设计天梯赛 L2-008 最长对称子串 (25分)
    团体程序设计天梯赛 L2-005 集合相似度 (25分)
    uva11401(Triangle Counting)
    UVA
  • 原文地址:https://www.cnblogs.com/shijieli/p/10342895.html
Copyright © 2011-2022 走看看