协程的核心点在于协程的使用,即只需要了解怎么使用协程即可;但如果你想了解协程是怎么实现的,就需要了解依次了解可迭代,迭代器,生成器了;
如果你只想看协程的使用,那么只需要看第一部分内容就行了;如果如果想理解协程,可以按照顺序依次阅读本博文,或者按照 迭代器-生成器-协程的顺序阅读。
协程
yield生成器是特殊的迭代器;
greenlet 对 yield 进行了封装;
而 gevent 对 greenlet 进行了封装;
gevent 遇见延时操作就换任务执行,这里的延时操作可以是等待服务器资源或者sleep等等;
上面的概念会在后面的知识点进行讲解;
greenlet实现多任务
要使用greenlet,首先要安装greenlet
pip3 install greenlet
greenlet实现多任务代码
greenlet实现多任务
但注意,这里其实是一个单线程;并且经过测试,这里最后几句不能使用 __main__ ,否则会报错;
gevent实现多任务
可以看到,greenlet已经可以实现协程了,但需要我们手动进行任务切换,这样会很麻烦,因此我们要学习gevent,在greenlet的基础上进行了封装,可以帮助我们实现自动切换任务;
要使用gevent,使用要进行安装
pip3 install gevent
gevent实现多任务代码
gevent实现多任务.py
运行结果:
运行结果
注意,在gevent中如果要使用sleep(),必须要使用 gevent.sleep();
存在一个问题当我们创建g1,g2,g3时,如果不小心全部创建了g1,结果和没写错几乎是一样的;
问题版运行结果
郑州妇科医院哪家好:https://yyk.familydoctor.com.cn/21521/
问题版运行结果
协程的核心在于利用延时操作去做其他的任务;
给gevent打补丁
当我们使用gevent的时候,如果要延时操作,比如等待网络资源或者time.sleep(),必须要使用 gevent.sleep(),即每处延时操作都需要改成gevent的延时;如果我们想,还是按照原来的写法,并且使用gevent,怎么实现呢?这个实收,我们解疑使用打补丁的方法。只需要给使用gevent的代码添加如下一行代码即可完成打补丁
from gevent import monkey
monkey.patch_all()
使用打补丁的方式完成协程的使用
给gevent打补丁.py
给gevent打补丁,使time.sleep(1)之类的耗时操作等效于gevent.sleep(1);
gevent.joinall()的使用
如果我们有很多函数要调用,那么岂不是得每次都先创建,在join(),gevent提供了一种简便方式;
gevent.joinall()的使用.py
协程使用小案例-图片下载器
协程的使用-图片下载器.py
进程,线程,线程对比
区别
进程是资源分配的单位
线程是操作系统调度的单位
进程切换需要的资源很最大,效率很低
线程切换需要的资源一般,效率一般(当然了在不考虑GIL的情况下)
协程切换任务资源很小,效率高
多进程、多线程根据cpu核数不一样可能是并行的,但是协程是在一个线程中 所以是并发。
多进程耗费的资源最多;
当我们python3运行一个py文件时,就是运行一个进程,进程中有一个默认的线程就是主线程,主线程拿着代码去执行;即进程是资源分配的单位,而线程才是真正拿着资源去执行,操作系统真正调度的就是线程;
一个进程里面有两个线程就是我们说的多线程的多任务方式,第二种多任务方式是多进程中有多线程;
线程的一大特点是可以利用某个线程在等待某个资源到来的时间去执行其他的任务;
在不考虑GIL的情况下,优先考虑协程,再考虑线程,再考虑进程;
进程是最稳定的,一个进程出问题了不会影响其他的进程,但耗费的资源较大;线程在切换任务时耗费的资源较线程少;协程可以利用线程在等待的时间做其他的事;
迭代器
迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
要理解协程的使用,首先要了解生成器;
要了解生成器,首先要理解迭代器;
推荐原来看过的一篇博客:一文彻底搞懂Python可迭代(Iterable)、迭代器(Iterator)和生成器(Generator)的概念 ,不过和本文关系不大,哈哈~
在了解迭代器之前,我们来认识两个单词
Iterable 可迭代的/可迭代/可迭代对象
Iterator 迭代器
可迭代
迭代器引入-for循环
In [1]: for i in [11,22,33]:
...: print(i)
11
22
33
In [2]: for i in "hhh":
...: print(i)
h
h
h
In [3]: for i in 10:
...: print(i)
...:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
in ()
----> 1 for i in 10:
2 print(i)
3
TypeError: 'int' object is not iterable # “int”对象不可迭代
使用for循环时,in后面的数据类型是可迭代的 才可以使用for循环,例如元组,列表,字符串等;不可迭代的,例如数字,小数点的;
判断是否可迭代
判断某个东西是否可迭代的,可以通过判断该数据类型是否为 Iterable 的子类,如果是则为可迭代;
isinstance 可以用来判断某对象是否是某类创建出来的;
比如我们要判断 a是否为A类创建出来的,可以使用 isinstance(a, A)进行判断;返回值为True,代表可迭代;
判断列表是否是可迭代的:
from collections import Iterable
isinstance([11,22,33], Iterable)
True
isinstance判断数据类型是否可迭代
In [6]: from collections import Iterable
In [7]: isinstance([11,22], Iterable)
Out[7]: True
In [8]: isinstance((11,22), Iterable)
Out[8]: True
In [9]: isinstance(10, Iterable)
Out[9]: False
元组,列表,字符串都是可迭代的;数字,小数不可迭代;
我们把可以通过for...in...这类语句迭代读取一条数据供我们使用的对象称之为可迭代对象(Iterable)。
自己定义的一个类,判断能不能用for?
自己创建一个类,满足能用for循环遍历的需求
不可迭代
class Classmate(object):
"""docstring for Classmate"""
def __init__(self):
self.names = list()
def add(self, name):
self.names.append(name)
classmate = Classmate()
classmate.add("张三")
classmate.add("李四")
classmate.add("王五")
for name in classmate:
print(name)
# TypeError: 'Classmate' object is not iterable
可迭代对象本质
我们分析对可迭代对象进行迭代使用的过程,发现每迭代一次(即在for...in...中每循环一次)都会返回对象中的下一条数据,一直向后读取数据直到迭代了所有数据后结束。那么,在这个过程中就应该有一个“人”去记录每次访问到了第几条数据,以便每次迭代都可以返回下一条数据。我们把这个能帮助我们进行数据迭代的“人”称为迭代器(Iterator)。
可迭代对象的本质就是可以向我们提供一个这样的中间“人”即迭代器帮助我们对其进行迭代遍历使用。
可迭代对象通过__iter__方法向我们提供一个迭代器,我们在迭代一个可迭代对象的时候,实际上就是先获取该对象提供的一个迭代器,然后通过这个迭代器来依次获取对象中的每一个数据.
那么也就是说,一个具备了__iter__方法的对象,就是一个可迭代对象。
如果你不理解上面的话,没关系,你只需要知道 “如果想要将自己定义的一个类变为可迭代的,那么只需要在这个类中定义一个 __iter__ 方法即可”。