美文网首页
并发编程-协程

并发编程-协程

作者: Yanl__ | 来源:发表于2019-10-29 21:37 被阅读0次
    1. 协程
      • greenlet模块 (gevent实现原理)
      • gevent模块 (注册协程,实现异步编程)
    2. 协程的应用
      eg:爬虫、socket聊天

    协程

    1. 概念

      • 能够在一个线程中实现并发效果。
      • 能够规避一些任务中的IO操作,在检测到IO就切换到其他任务
    2. 对比进程和线程

      • 进程和线程的任务切换由操作系统完成
      • 协程任务之间的切换由程序(代码)完成,只有遇到协程模块能识别的IO操作的时候,程序才会进行任务切换,实现并发的效果
      • 进程、线程、协程的开启数量:
        cpu核心数:4
        进程 开5个 (cpu数+1)
        线程 开20个 (进程数4)
        协程 开5w个 (进程数
        线程数*500)一个线程能开500个协程

    greenlet模块

    真正的协程模块就是使用greenlet完成切换的

    # 多任务之间切换的方法 greenlet模块   (协程)
    from greenlet import greenlet
    def eat():
        print('eating start')
        g2.switch()  # 切换到对应的方法,接着执行 
        print('eating end')
    
    
    def play():
        print('playing start')
    
    g2 = greenlet(play)
    g1 = greenlet(eat)
    g1.switch()
    

    gevent模块

    • gevent 感知不到其他模块(time或其他阻塞)的阻塞。只能用gevent.sleep()
    • 不过可以通过打补丁实现感知time 、 socket的阻塞 from gevent import monkey;monkey.patch_all() 此行代码需要放在文件的开头,在import time之前
    gevent方法:
    1. g1 = gevent.spawn(func)将func注册到协程 spawn(func, *args, **kwargs)
    2. g1.join()激活协程,等待g1结束
    3. g1.value拿到协程对象的返回值(就是协程执行的func的返回值)
    4. gevent.joinall(iterator) 将多个协程对象保存为一个可迭代对象,传入joinall方法,等待所有协程结束
    import gevent
    def eat():
        print('eating start')
        gevent.sleep(2)  # gevent 感知不到其他模块(time或其他阻塞)的阻塞。只能用gevent.sleep()
        # 不过可以通过打补丁实现感知time 、 socket的阻塞  from gevent import monkey;monkey.patch_all()
        print('eating end')
    
    def play():
        print('playing start')
        gevent.sleep(2)
        print('playing end')
    
    g1 = gevent.spawn(eat)  # 将func注册在协程中
    g2 = gevent.spawn(play)
    g1.join()  # 等待协程执行完毕
    g2.join()
    
    from gevent import monkey;monkey.patch_all()
    import time
    import gevent
    def eat(i):
        print('eating start')
        time.sleep(2)  # gevent 感知不到其他模块(time或其他阻塞)的阻塞。只能用gevent.sleep()
        # 不过可以通过打补丁实现感知time 、 socket的阻塞  from gevent import monkey:monkey.patch_all()
        print('eating end')
        return '%send'%i
    
    def play():
        print('playing start')
        time.sleep(2)
        print('playing end')
    
    g_list = []
    ge_list = []
    for i in range(10):
        ge = gevent.spawn(play)
        ge_list.append(ge)
    
    for i in range(20):
        g = gevent.spawn(eat, i)
        g_list.append(g)
    
    for ge in ge_list:            # 等待所有协程执行完毕的 方法一
        ge.join() 
    gevent.joinall(g_list)        # 等待所有协程执行完毕的 方法二
    
    for g in g_list:
        print(g.value)  # 获得func的返回值
    

    协程的应用

    爬虫

    获取网页内容

    方法一:requests
    import requests
    def get_content(url):
        content = requests.get(url)        # 通过request.get到的网页内容是无格式的
        return content.content.decode('utf-8')
    
    url = 'http://www.baidu.com'
    print(get_content(url))
    
    方法二:urlopen
    from urllib.request import urlopen
    def get_content(url):
        content = urlopen(url)        # urlopen到的网页内容是有格式的
        return content.read().decode('utf-8')
    
    url = 'http://www.baidu.com'
    print(get_content(url))
    

    协程来爬取网页

    from gevent import monkey;monkey.patch_all()
    import gevent
    import ssl
    from urllib.request import urlopen, Request
    def get_content(url):
        context = ssl._create_unverified_context()
        headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20100101 Firefox/23.0'}
        get_url = Request(url=url, headers=headers)
        content = urlopen(get_url, context=context)        # urlopen到的网页内容是有格式的
        text = content.read().decode('utf-8')
        return len(text)
    
    url_list = ['http://www.baidu.com',
                'http://www.jianshu.com',
                'http://www.csdn.com',
                'http://www.cctv.com',
                'http://www.sogou.com']
    # 通过协程来并发获得网页内容的长度
    g_list = []
    for i in range(len(url_list)):
        g = gevent.spawn(get_content, url_list[i])
        g_list.append(g)
    for g in g_list:g.join()
    for g in g_list:print(g.value)
    

    socket

    server端

    from gevent import monkey;monkey.patch_all()
    import gevent
    import socket
    sk = socket.socket()
    sk.bind(('127.0.0.1', 8080))
    sk.listen()
    
    def talk(conn):
        conn.send(b'hello')
        msg = conn.recv(1024).decode('utf-8')
        print(msg)
        conn.close()
    
    
    while True:
        conn, addr = sk.accept()
        gevent.spawn(talk, conn)
    
    
    sk.close()
    

    client端

    import socket
    sk = socket.socket()
    sk.connect(('127.0.0.1', 8080))
    
    msg = sk.recv(1024).decode('utf-8')
    print(msg)
    send_msg = input('>>>')
    sk.send(send_msg.encode('utf-8'))
    sk.close()
    

    相关文章

      网友评论

          本文标题:并发编程-协程

          本文链接:https://www.haomeiwen.com/subject/awjwvctx.html