协程 - Go语言中文社区

协程


先来看一下yield

def producer(name):
    for i in range(1,10):
        print("[%s] 生产了包子[%s]" %(name, i))
        yield  '包子[%s]'%i

def consumer(name):
    while True:
        bz = yield
        print("[%s]取到了[%s],并且吃了它" %(name, bz))

if __name__ == '__main__':
    p = producer('Alex')
    c1 = consumer('xiaoming')
    # c2 = consumer('small')
    c1.__next__()
    # c2.__next__()
    for i in p:
        print('i:',i)
        c1.send(i)
        # c2.send(i)

线程和进程的操作是由程序触发系统接口,最后的执行者是系统;协程的操作则是程序本身。

协程存在的意义:对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保存状态,下次继续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序。

协程的适用场景:当程序中存在大量不需要CPU的操作时(IO),适用于协程;

greenlet

greenlet是一个用C实现的协程模块。一句话来说明greenlet的实现原理:通过栈的复制切换来实现不同协程之间的切换

from greenlet import greenlet

def test1():
    print(12)     #2
    gr2.switch()  #3
    print(34)     #6
    # gr1.switch()

def test2():
    print(56)      #4
    gr1.switch()   #5
    print(78)

gr1 = greenlet(test1)    #生成一个greenlet的对象
gr2 = greenlet(test2)    #再生成一个greenlet对象
gr1.switch()          #1

输出为:
12
56
34

当创建一个greenlet时,首先初始化一个空的栈, switch到这个栈的时候,会运行在greenlet构造时传入的函数(首先在test1中打印 12), 如果在这个函数(test1)中switch到其他协程(到了test2 打印34),那么该协程会被挂起,等到切换回来(在test2中切换回来 打印34)。当这个协程对应函数执行完毕,那么这个协程就变成dead状态。

注意 上面没有打印test2的最后一行输出 78,因为在test2中切换到gr1之后挂起,但是没有地方再切换回来。这个可能造成泄漏,后面细说。

gevent

Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程,在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程。 Greenlet全部运行在主程序操作系统进程的内部,但它们被协作式地调度。

import gevent

def test1():
    print('in test1')
    gevent.sleep(1)
    print(111)

def test2():
    print('in test2')
    gevent.sleep(0)
    print(222)

gevent.joinall([gevent.spawn(test1),
                gevent.spawn(test2),])

print("end")

join的意义

import gevent
import time

def talk(msg):
    print(msg)
    gevent.sleep(0)
    print(msg)

g1 = gevent.spawn(talk, 'bar')
gevent.sleep(0)
# gevent.joinall([g1,])  #加上join将输出  bar bar  end
print('end')

输出:
bar
end

协程gevent简单的爬网页

from gevent import monkey
import gevent, time
from urllib import request

monkey.patch_all()   #遇到io操作就做上标记

def f(url):
    print('GET: %s' %url)
    res = request.urlopen(url)
    data = res.read()
    print('%d bytes received from %s.' %(len(data), url))

start_time = time.time()
gevent.joinall([
        gevent.spawn(f, 'https://www.python.org/'),
        gevent.spawn(f, 'https://www.yahooar.com/'),
        gevent.spawn(f, 'https://github.com/'),
])
print('time:',time.time() - start_time)
Synchronous & Asynchronous Execution 同步vs异步执行

并发的核心思想是一个更大的任务可以分解成多个子任务,其运行不依赖于其他任务的集合,因此可以异步运行 ,而不是一个在时间 同步。两个执行程序间的转换是一个关联转换。

在gevent中一个关联转换可以通过 yielding 来实现.在这个例子,两个程序的转换是通过调用 gevent.sleep(0).

import gevent
import random

def task(pid):
    """
    Some non-deterministic task
    """
    gevent.sleep(random.randint(0,2))
    print('Task', pid, 'done')

def synchronous():
    for i in range(1,10):
        task(i)

def asynchronous():
    coroutine_list = [gevent.spawn(task, i) for i in range(10)]
    gevent.joinall(coroutine_list)

print('Synchronous:')
synchronous()

print('Asynchronous:')
asynchronous()

深度分析gevent运行流程

版权声明:本文来源简书,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://www.jianshu.com/p/447e8cc10d62
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-01-12 13:03:24
  • 阅读 ( 1239 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

推荐文章

猜你喜欢