美文网首页
Python 协程学习

Python 协程学习

作者: vckah | 来源:发表于2018-07-11 10:19 被阅读0次

    协程本质上仍然是一个线程,其能够在多个任务之间切换来节省一些 IO 时间。

    def consumer():
        while True:
            x = yield
            print("处理了数据")
    def producer():
        c = consumer()
        next(c)
        for i in range(10):
            print("生产了数据:", i)
            c.send(i)
    producer()
    # 一边生产一边消耗,它并不能节省时间
    

    greenlet 模块来支持

    from greenlet import greenlet
    def eat():
        print('eating start')
        g2.switch()
        print('eating end')
    
    def play():
        print('playing start')
        g1.switch()
        print('playing end')
        g2.switch()
    
    g1 = greenlet(eat)
    g2 = greenlet(play)
    g1.switch()
    

    协程一般是为了规避 IO 时间的,对于计算型的,它并不能减少时间。一个线程最多可以起 500 个协程。
    gevent 是一个支持协程的模块。gevent 的优势在于:用同步的方法来写异步的代码。一句话来阐述:使用 IO 多路复用对文件描述符的事件进行监听,从而使协程透明切换。

    from gevent import monkey
    monkey.patch_all()   <<--- 注意这个猴子补丁,它捕获程序中的 IO 时间,遇到会自动切换
    import time
    import gevent
    def eat():
        print('eating start')
        time.sleep(1)
        print('eating end')
    
    def play():
        print('playing start')
        time.sleep(1)
        print('playing end')
    
    g1 = gevent.spawn(eat)
    g2 = gevent.spawn(play)
    g1.join()
    g2.join()
    # 可以查看它的线程名,发现它的名字是 DummyThread-1
    # g.join(g_lst)
    

    一般用在爬虫中。协程实现一个 socket 服务端

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

    gevent 提供了事件,感觉与线程的那个事件差不多:

    import gevent
    from gevent import Event
    
    evt = Event()
    
    def setter():
        print('wait for me')
        gevent.sleep(3)
        print('done')
        evt.set()
    
    def waiter():
        print('wait...')
        evt.wait()
        print('finist')
    gevent.joinall(
        [
            gevent.spawn(setter),
            gevent.spawn(waiter),
            gevent.spawn(waiter),
            gevent.spawn(waiter),
        ]
    )
    

    结果:

    wait for me
    wait...
    wait...
    wait...
    done
    finist
    finist
    finist
    

    还有一个 AsyncResult 事件,用于在唤醒时传递消息。

    from gevent.event import AsyncResult
    aevt = AsyncResult()
    
    def setter():
        print('wait ')
        gevent.sleep(3)  # 3秒后唤醒所有在evt上等待的协程
        print('done')
        aevt.set('hello') # 唤醒,并传递消息
    
    def waiter():
        print("I‘ll wait for you")
        message = aevt.get()  # 等待,并在唤醒时获取消息
        print("Got wake up message: %s"% message)
    gevent.joinall(
        [
            gevent.spawn(setter),
            gevent.spawn(waiter),
            gevent.spawn(waiter),
            gevent.spawn(waiter),
        ]
    )
    

    结果:

    wait 
    I‘ll wait for you
    I‘ll wait for you
    I‘ll wait for you
    done
    Got wake up message: hello
    Got wake up message: hello
    Got wake up message: hello
    

    gevent 提供了一个队列对象,可以在协程之间安全的访问

    import gevent
    from gevent.queue import Queue
    
    products = Queue()
    
    def consumer(name):
        while not products.empty():
            print("%s got product % s"% (name, products.get()))
            gevent.sleep(0)
    
        print("%s quit"% name)
    
    def producer():
        for i in range(1, 10):
            products.put(i)
    gevent.joinall([
        gevent.spawn(producer),
        gevent.spawn(consumer, 'steve'),
        gevent.spawn(consumer, 'john'),
        gevent.spawn(consumer, 'nancy'),
    ])
    

    结果:

    steve got product 1
    john got product 2
    nancy got product 3
    steve got product 4
    john got product 5
    nancy got product 6
    steve got product 7
    john got product 8
    nancy got product 9
    steve quit
    john quit
    nancy quit
    

    还有一个 BoundedSemaphore 计数信号量:

    import gevent
    from gevent.lock import BoundedSemaphore
    
    sem = BoundedSemaphore(2)
    
    def worker(n):
        sem.acquire()
        print('Worker %i acquired semaphore' % n)
        gevent.sleep(2)
        sem.release()
        print('Worker %i released semaphore' % n)
    
    gevent.joinall([gevent.spawn(worker, i) for i in range(0, 6)])
    

    结果:

    Worker 0 acquired semaphore
    Worker 1 acquired semaphore
    Worker 0 released semaphore
    Worker 1 released semaphore
    Worker 2 acquired semaphore
    Worker 3 acquired semaphore
    Worker 2 released semaphore
    Worker 3 released semaphore
    Worker 4 acquired semaphore
    Worker 5 acquired semaphore
    Worker 4 released semaphore
    Worker 5 released semaphore
    

    协程的本地变量:

    import gevent
    from gevent.local import local
    
    data = local()
    
    def f1():
        data.x = 1
        print(data.x)
    
    def f2():
        try:
            print(data.x)
        except AttributeError:
            print('x is not visible')
    
    gevent.joinall([
        gevent.spawn(f1),
        gevent.spawn(f2)
    ])
    

    结果:

    1
    x is not visible
    

    本地变量,当一个协程在内部使用了 data 对象后,那么它就在其它协程内不可见了。

    相关文章

      网友评论

          本文标题:Python 协程学习

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