协程

协程与线程差异
  在实现多任务时, 线程切换从系统层面远不止保存和恢复 CPU上下文这么简单。 操作系统为了程序运行的高效性每个线程都有自己缓存Cache等等数据,操作系统还会帮你做这些数据的恢复操作。 所以线程的切换非常耗性能。但是协程的切换只是单纯的操作CPU的上下文,所以一秒钟切换个上百万次系统都抗的住。
  
原理
  协程是python个中另外一种实现多任务的方式,只不过比线程更小占用更小执行单元(理解为需要的资源)。为什么说它是一个执行单元,因为它自带CPU上下文。这样只要在合适的时机, 我们可以把一个协程 切换到另一个协程。 只要这个过程中保存或恢复 CPU上下文那么程序还是可以运行的。

1.简单实现协程
例:

import time


def work1():
    while True:
        print('work1----------')
        yield
        time.sleep(0.5)


def work2():
    while True:
        print('work2---')
        yield
        time.sleep(0.5)


if __name__ == '__main__':
    w1 = work1()
    w2 = work2()
    while True:
        next(w1)
        next(w2)

将work函数用yield改写为生成器,并且可以保存暂停当前函数内容,下次执行时继续,使得work1和work2交替执行。

2.greenlet创建
例:

from greenlet import greenlet
import time


def test1():
    while True:
        print("---A--")
        gr2.switch()
        time.sleep(0.5)


def test2():
    while True:
        print("---B--")
        gr1.switch()
        time.sleep(0.5)


gr1 = greenlet(test1)
gr2 = greenlet(test2)
# 切换到gr1中运行
gr1.switch()

开发者使用switch()方法手动切换协程,也实现了两个函数交替执行

3.gevent创建
自动调度的协程,识别耗时操作自动进行协程切换
gevent.spawn(函数名, 参数)
join:使主线程等待写执行完毕再结束
joinall(列表):让主线程等待协程执行完毕再结束

import gevent


def work1():
    for i in range(5):
        print("work1 -----1")
        gevent.sleep(0.5)


def work2():
    for i in range(5):
        print("work2 -----2")
        gevent.sleep(0.5)


# 创建携程并指派任务
# g1 = gevent.spawn(work1)
# g2 = gevent.spawn(work2)
# 等待协程执行完成再关闭主线程
# g1.join()
# g2.join()
# 等价于
gevent.joinall([gevent.spawn(work1), gevent.spawn(work2)])

4.注意
  gevent中不识别time.sleep()等方法,它有自己的sleep()方法,如果要用time.sleep()等方法,需要打猴子补丁:

from gevent import monkey
monkey.patch_all()

查看当前执行的协程名称:gevent.getcurrent()

5.案例(并发下载器):

import urllib.request
import gevent


def img_download(url, file_name):
    try:
        img_file = urllib.request.urlopen(url)
        with open(file_name, "wb") as file:
            while True:
                img_data = img_file.read(1024)
                if img_data:
                    file.write(img_data)
                else:
                    break
    except:
        print("文件%s下载异常" % file_name)
    else:
        print("文件%s下载成功" % file_name)


if __name__ == '__main__':
    img_url1 = "http://img.mp.itc.cn/upload/20170716/8e1b835f198242caa85034f6391bc27f.jpg"
    img_url2 = "http://pic1.wed114.cn/allimg/180227/1023303521-1.gif"
    img_url3 = "http://image.uczzd.cn/11867042470350090334.gif?id=0&from=export"
    gevent.joinall([
        gevent.spawn(img_download, img_url1, "1.gif"),
        gevent.spawn(img_download, img_url2, "2.gif"),
        gevent.spawn(img_download, img_url3, "3.gif")
    ])

进程、线程、协程区别

1.进程:资源分配的基本单位,重量级,单独分配内存
2.线程:任务调度的基本单位,本身不拥有资源,占有很少的运行资源
3.协程:微线程,在不创建新线程的基础上,执行多任务