zoukankan      html  css  js  c++  java
  • python并发_协程

    在操作系统中进程是资源分配的最小单位, 线程是CPU调度的最小单位。

    协程:是单线程下的并发,又称微线程,纤程。英文名Coroutine。一句话说明:协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的。也就是说程序员用代码来控制切换.

    参考: http://www.cnblogs.com/Eva-J/articles/8324673.html

    # 进程 启动多个进程 进程之间是由操作系统负责调用
    # 线程 启动多个线程 真正被CPU执行的最小单位实际是线程
        # 开启一个线程 创建一个线程 寄存器 堆栈
        # 关闭一个线程
    
    # 协程
        # 本质上是一个线程
        # 能够在多个任务之间切换来节省一些IO时间
        # 协程中任务之间的切换也消耗时间,但是开销要远远小于进程线程之间的切换
    # 实现并发的手段
    
    import time
    def consumer():
        while True:
            x = yield
            time.sleep(1)
            print('处理数据 :',x)
    
    def producer():
        c = consumer()
        next(c)
        for i in range(10):
            time.sleep(1)
            print('生产数据:',i)
            c.send(i)
    
    # 这个生产者消费者模型 模拟了程序的来回切换, 但是不能规避IO时间
    producer()

    使用pip3 install greenlet 和 pip3 install gevent 安装好模块,继续:

    # 真正的协程模块就是使用greenlet完成的切换
    from greenlet import greenlet
    
    def eat():
        print('eating start')
        g2.switch()   # 切换到g2
        print('eating end')
        g2.switch()
    
    def play():
        print('playing start')
        g1.switch()  # 切换到g1
        print('playing end')
    
    g1 = greenlet(eat)  # 委托给g1
    g2 = greenlet(play)
    g1.switch()
    • greenlet可以实现协程,不过每一次都要人为的去指向下一个该执行的协程,显得太过麻烦。python还有一个比greenlet更强大的并且能够自动切换任务的模块gevent

    参考:https://www.cnblogs.com/PrettyTom/p/6628569.html

    # 协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的。
    import time
    import gevent
    
    def eat():
        print('eating start')
        # time.sleep(1)      # gevent 不能感知到time.sleep时间
        gevent.sleep(1)
        print('eating end')
    
    def play():
        print('playing start')
        gevent.sleep(1)
        print('playing end')
    
    g1 = gevent.spawn(eat)
    g2 = gevent.spawn(play)
    g1.join()
    g2.join()

    gevent的正确方式:

    ## 导入这句,将所有模块中的阻塞IO都打成一个包。就可以感知 time.sleep
    from gevent import monkey;monkey.patch_all()
    import time
    import gevent
    import threading
    
    def eat():
        print(threading.current_thread().getName())  # Dummy 假的,虚拟的。
        print(threading.current_thread())
        print('eating start')
        time.sleep(1.2)
        print('eating end')
    
    def play():
        print(threading.current_thread().getName())
        print(threading.current_thread())
        print('playing start')
        time.sleep(1)
        print('playing end')
    
    g1 = gevent.spawn(eat)   # 注册到协程,遇到IO将自动切换
    g2 = gevent.spawn(play)
    # g1.join()
    # g2.join()
    gevent.joinall([g1,g2])
    print('master')
    # 进程和线程的任务切换由操作系统完成
    # 协程任务之间的切换由程序(代码)完成,只有遇到协程模块能识别的IO操作的时候,程序才会进行任务切换,实现并发的效果

    同步和异步:

    # 同步 和 异步
    from gevent import monkey;monkey.patch_all()
    import time
    import gevent
    
    def task(n):
        time.sleep(1)
        print(n)
    
    def sync():
        for i in range(5):
            task(i)
    
    def async():
        g_lst = []
        for i in range(5):
            g = gevent.spawn(task,i)
            g_lst.append(g)
        gevent.joinall(g_lst)  # for g in g_lst:g.join()
    
    sync()   # 同步
    async()  # 异步

    爬虫时使用协程并发

    # 协程 : 能够在一个线程中实现并发效果的概念
        #    能够规避一些任务中的IO操作
        #    在任务的执行过程中,检测到IO就切换到其他任务
    
    # 多线程 被弱化了
    # 协程 在一个线程上 提高CPU 的利用率
    # 协程相比于多线程的优势 切换的效率更快
    
    # 爬虫的例子
    # 请求过程中的IO等待
    from gevent import monkey;monkey.patch_all()
    import gevent
    from urllib.request import urlopen    # 内置的模块
    
    def get_url(url):
        response = urlopen(url)
        content = response.read().decode('utf-8')
        return len(content)
    
    g1 = gevent.spawn(get_url,'http://www.baidu.com')
    g2 = gevent.spawn(get_url,'http://www.sogou.com')
    g3 = gevent.spawn(get_url,'http://www.taobao.com')
    g4 = gevent.spawn(get_url,'http://www.hao123.com')
    g5 = gevent.spawn(get_url,'http://www.cnblogs.com')
    gevent.joinall([g1,g2,g3,g4,g5])
    print(g1.value)
    print(g2.value)
    print(g3.value)
    print(g4.value)
    print(g5.value)
    
    ret = get_url('http://www.baidu.com')
    print(ret)
  • 相关阅读:
    一个有关Golang Deferred Function 执行顺序的问题
    Golang闭包的坑
    Golang new() vs make()
    修改linux文件的mtime
    Golang通过反射获取结构体的标签
    rpm --qf 命令
    elasticsearch 5.1 认证过期 (your license has expired)
    Heka GeoIpDecoder 配置
    Heka 编译安装后 运行报错 panic: runtime error: cgo argument has Go pointer to Go pointer
    github.com/oschwald/maxminddb-golang 安装报错
  • 原文地址:https://www.cnblogs.com/frx9527/p/gevent.html
Copyright © 2011-2022 走看看