python中的协程

目录

 

协程

1.yield的简单实现

2.greenlet 模块

3.gevent模块


协程

 

协程:协助程序,线程和进程都是抢占式特点,线程和进程的切换我们是不能参与的。

而协程是非抢占式特点,协程也存在着切换,这种切换是由我们用户来控制的。

协程主解决的是IO的操作。

 

协程,又称微线程,纤程。英文名Coroutine

优点1: 协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。

优点2: 不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。

因为协程是一个线程执行,那怎么利用多核CPU呢?最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。

1.yield的简单实现

前面我们学习生成器的时候知道,这个只有在调用的时候才执行

import time


def test1():
    while True:
        print('test1....')
        time.sleep(1)
        yield


def test2():
    while True:
        print('test2....')
        time.sleep(1)
        yield


def main():
    g1 = test1()
    g2 = test2()
    while True:
        next(g1)
        next(g2)


if __name__ == '__main__':
    main()

输出为:

python中的协程_第1张图片

协和调用:

# 使用协程

# import time
#
#
# def test1():
#     while True:
#         print('test1....')
#         time.sleep(1)
#         yield
#
#
# def test2():
#     while True:
#         print('test2....')
#         time.sleep(1)
#         yield
#
#
# def main():
#     g1 = test1()
#     g2 = test2()
#     while True:
#         next(g1)
#         next(g2)
#
#
# if __name__ == '__main__':
#     main()

import time


def test1():
    for i in range(3):
        print('test1....',i)
        yield
        time.sleep(1)


def test2():
    for i in range(3):
        print('test2....',i)
        yield


def main():
    g1 = test1()
    g2 = test2()
    for i in range(3):
        next(g1)
        next(g2)
    print('结束了')


if __name__ == '__main__':
    main()

输出为:

python中的协程_第2张图片

2.greenlet 模块

greenlet是一个用C实现的协程模块,相比与python自带的yield,它可以使你在任意函数之间随意切换,而不需把这个函数先声明为generator

安装 :pip3 install greenlet。如果引入的时候还是报错,使用pycharm进行下载安装,

选择 file --> settings --> Project:项目名称 --> Project Interpreter-->…

 

python中的协程_第3张图片

python中的协程_第4张图片

greenlet模块

- gr1=greenlet(目标函数)

- gr1.switch() 切换执行

代码:

from greenlet import greenlet


def test1():
    print('test1....01')
    gr2.switch()
    print('test1....02')
    gr2.switch()


def test2():
    print('test2....01')
    gr1.switch()
    print('test2....02')


# 1、将要执行的函数封装到greenlet对象中
gr1 = greenlet(test1)
gr2 = greenlet(test2)
# 2、想先执行哪个函数就可以对象.swith()方法进行执行
gr1.switch()

输出为:

 

python中的协程_第5张图片

 

使用协程

import greenlet
import time


def test1():
    for i in range(5):
        print('test1...', i)
        g2.switch()  # 切换到g2
        time.sleep(1)


def test2():
    for i in range(5):
        print('test2...', i)
        g1.switch()  # 切换到g1


if __name__ == '__main__':
    g1 = greenlet.greenlet(test1)
    g2 = greenlet.greenlet(test2)

    g1.switch()  # 让程序去执行g1
    print('程序结束了。。。')

输出为:

python中的协程_第6张图片

3.gevent模块

import gevent


# 如果程序中没有耗时操作就顺序执行。
def test1():
    for i in range(5):
        print('test1...', i)
        gevent.sleep(1)  # 使用耗时模块可以自动操作


def test2():
    for i in range(5):
        print('test2...', i)
        gevent.sleep(1)


if __name__ == '__main__':
    g1 = gevent.spawn(test1)
    g2 = gevent.spawn(test2)
    g1.run()
    print('程序结束了')

输出为:

python中的协程_第7张图片

如上代码所示,gevent模块中自带了sleep耗时函数,当使用这个耗时函数时,cpu会跳转到另一个就绪的程序,达到人工设置让其自动切换的功能。

如果需要使用time.sleep()耗时的话,需要打一个补丁

from gevent import monkey

monkey.patch_all()

打完之后就可以实现了

import gevent
import time
from gevent import monkey

monkey.patch_all()



def test1():
    for i in range(5):
        print('test1...', i)
        time.sleep(1)


def test2():
    for i in range(5):
        print('test2...', i)
        time.sleep(1)


if __name__ == '__main__':
    g1 = gevent.spawn(test1)
    g2 = gevent.spawn(test2)
    g1.run()
    print('程序结束了')

输出为:

python中的协程_第8张图片

gevent.joinall

使用joinall可以一次生成多个

import gevent
import time
from gevent import monkey
monkey.patch_all()

def test1():
    for i in range(5):
        print('test1...',i)
        time.sleep(1)

def test2():
    for i in range(5):
        print('test2...', i)
        time.sleep(1)

if __name__ == '__main__':
    gevent.joinall([
        gevent.spawn(test1),
        gevent.spawn(test2)
    ])

输出和上面一样

 

 

图片下载器

import requests
import gevent

def download_img(url, img_name):
    response = requests.get(url)  # 获取图片中的二进制数据
    with open('img/' + img_name, mode='wb') as f:
        f.write(response.content)

if __name__ == '__main__':
    url='https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1548222339580&di=8033e859c88d8de0dd07fb0908a511ab&imgtype=0&src=http%3A%2F%2Fimgm.gmw.cn%2Fattachement%2Fjpg%2Fsite2%2F20170222%2Ff44d307589e71a1729e422.jpg'
    url1='https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1548222339580&di=d21ac1313363549c11498991e2446e85&imgtype=0&src=http%3A%2F%2Fcn.toluna.com%2Fdpolls_images%2F2017%2F09%2F21%2F371665b2-af55-43b9-a25e-ba30eea4a737_x400.jpg'

    gevent.joinall({
        gevent.spawn(download_img,url,'panda1.jpg'),
        gevent.spawn(download_img,url1,'panda2.jpg')
    })

就下载好了:

你可能感兴趣的:(python中的协程)