协程的学习详解编程语言

这篇博客会用三种方式实现协程

1、yield

2、greenlet

3、gevent

我们先来看下第一种方式,使用yield实现协程

1、先来复习一下yield,如果一个函数中有yield,那么他就是一个生成器

def test(): 
    print("ok") 
    yield 
# 如果一个函数有yield,那么他就不是一个普通的函数,而是一个生成器,这个yield相当于普通函数的return 
 
 
 
test() 
# print(test()) 
#如果是一个生成器,那么这段代码不会去执行函数test的,而仅仅是生成一个生成器对象,我们通过print就可以看的出来 
 
# 如果想执行这个生成器,只能生成器对象调用next方法来执行生成器这个函数 
 
# next(test()) 
# 这样就执行了上面的生成器的函数,上面这个例子中yield后没有任何值,那么他们返回值就是默认的None,我们用下面的代码就可以获取到yield后面的值 
#在这个例子中,我们可以看到a就可以接受到yield后面的值,我们打印a就可以到a的值为None 
a = next(test()) 
print(a) 
 
def test2(): 
    print(id(test2)) 
    yield 2 
    # next方法执行这里到就会返回一个值2,然后退出函数 
 
b = next(test2()) 
print(b) 
# 这里yield后面的值为2,那么我们打印b就可以得到b的值为2 
 
# 如果函数中有一个yield就可以执行一个next方法,如果有2个,就可以执行2个next方法,一次类推 
 
# yield的作用:相当于函数中的return,next方法执行到这里,就会退出,如果在来一个next,就接着yield后面的代码继续执行 
 
 
 
# 上面的例子,只能函数返回值,下面的例子中,我们可以给这个函数传递值 
 
 
def test3(): 
    print("test23") 
    count = yield 3 
    print(count) 
    yield 
a = test3() 
c = next(a) 
 
# 1、通过next方法进入生成器,执行到yield 3这段代码,就执行结束 
print(c) 
# 这里可以打印c的值为yield后面的值,为3 
 
c = a.send("aa") 
# 这里通过send再次进入,通过send方法传递一个aa进去,这个aa就会赋值给你yield前面的变量,也就是count 
print(c) 

  

在来看下通过yiled实现协程,我们用yield来实现一个吃包子的例子

# 协程:在单线程下实现并发,协程是一种用户态的轻量级线程 
 
 
# 好处 
# 1、无需上下文切换,因为只有一个线程,所以无需要在不同的cpu之间切换 
# 2、无需加锁 
# 3、方便切换控制流,简化编程模型 
# 4、高并发+高扩展+低成本,一个cpu并发上万个协程都是没问题的 
 
# 资源消耗 
# 进程>线程>协程 
 
 
# 
# 不好的地方 
# 1、由于是在单线程下实现的协程,那么他就无法利用多核的优势,可以通过多进程+协程的方式实现,进程可以利用到多核的优势,开多个进程,每个进程开一个线程,在协程在开多个协程来实现 
# 2、如果出现阻塞,则会阻塞整个程序 
import time 
 
def eat(): 
    print("老子来要吃包子了") 
    while True: 
        num = yield 
        print("我吃的包子是{0}".format(num)) 
 
 
 
def  create(name): 
    num = 1 
    # e1 = eat() 
    # next(e1) 
    # 1、进入生成器的方式1 
    # e1.send(None) 
    # 2、进入生成器的方式2 
 
 
    # 第一次进入生成器,不能用send传参的方法进入生成器,会报错的,我们只能用2中方式进入生成器 
    # 1、next(e1) 
    # 2、e1.send(None),用send传参数,传一个none 
    print("{0}要来做包子了".format(name)) 
    while True: 
        time.sleep(0.1) 
        print("我做的包子是{0}".format(num)) 
        e1.send(num) 
        num = num + 1 
 
if __name__ == '__main__': 
    e1 = eat() 
    next(e1) 
    create("2B",) 

  

2、在看通过greenlet实现协程,遇到switch就切换

# import gevent 
from greenlet import greenlet 
 
 
 
 
def test1(): 
    print(12) 
    g2.switch() 
    print(34) 
    g2.switch() 
 
def test2(): 
    print("56") 
    g1.switch() 
    print("78") 
 
if __name__ == '__main__': 
    g1 = greenlet(test1) 
    g2 = greenlet(test2) 
    g1.switch() 

  

3、在看通过gevent实现协程,我们用gevent.sleep模拟io阻塞

import gevent 
import time 
def test1(): 
    n = 1 
    print("这是test1函数的第{1}句{0}".format(time.ctime(),n)) 
    gevent.sleep(2) 
    print("这是test1函数的第{1}句{0}".format(time.ctime(),n + 1)) 
 
def test2(): 
    n = 1 
    print("这是test2函数的第{1}句{0}".format(time.ctime(),n)) 
    gevent.sleep(1) 
    print("这是test2函数的第{1}句{0}".format(time.ctime(),n + 1)) 
 
 
 
if __name__ == '__main__': 
    gevent.joinall( 
        [ 
            gevent.spawn(test1), 
            gevent.spawn(test2) 
        ] 
    ) 

  

我们在看一个使用gevent实例协程的爬虫的例子

import gevent 
from urllib.request import urlopen 
import time 
from gevent import monkey 
monkey.patch_all() 
# 这一句是一个补丁,打上这个补丁,切换就会更快一些,主要是windows上起作用 
 
def test(url): 
    print("我要准备跑网址【{0}】".format(url)) 
    resp = urlopen(url) 
    data = resp.read() 
    print("网址【{0}】的长度是【{1}】".format(url,len(data))) 
 
 
test_list = ['https://www.python.org/','https://www.126.com/','https://www.baidu.com/'] 
 
if __name__ == '__main__': 
    start1_time = time.time() 
    for i in range(20): 
        gevent.joinall( 
            [ 
                gevent.spawn(test,test_list[0]), 
                gevent.spawn(test, test_list[1]), 
                gevent.spawn(test, test_list[2]), 
            ] 
        ) 
    end1_time = time.time() 
 
 
    start_time = time.time() 
    for i in range(20): 
        for url in test_list: 
            test(url) 
    end_time = time.time() 
    print("协程的时间时间是{0}".format(end1_time - start1_time)) 
    print("函数话费的时间是{0}".format(end_time - start_time)) 

  

原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/20849.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论