OK 这一篇主要是协程的,还落下了点线程的方法,先说线程,
线程池顾名思义就是跟是跟进程池一样的
到这里就差我们的线程池没有讲了,我们用一个新的模块给大家讲,早期的时候我们没有线程池,现在python提供了一个新的标准或者说内置的模块,这个模块里面提供了新的线程池和进程池,之前我们说的进程池是在multiprocessing里面的,现在这个在这个新的模块里面,他俩用法上是一样的。
为什么要将进程池和线程池放到一起呢,是为了统一使用方式,使用threadPollExecutor和ProcessPollExecutor的方式一样,而且只要通过这个concurrent.futures导入就可以直接用他们两个了
import time
from threading import current_thread #这里还是要导入线程的current_thread方法,因为要用。
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor #这里直接就把进程,线程一起导入了,省事!呵呵呵。。。
def f1(n,s):
time.sleep(1)
# print('%s号子线程'%current_thread().ident)
print(n,s)
return
if __name__ == '__main__':
tp = ThreadPoolExecutor(4) #线程池的方法(4)个线程
# tp = ProcessPoolExecutor(4)
# tp.map(f1,range(10)) #异步提交任务,参数同样是任务名称,可迭代对象
res_list = []
for i in range(10):
res = tp.submit(f1,i,'baobao') #submit是给线程池异步提交任务,
#print(res)
res.result() #取值这一步相当于把线程变成了一个同步的状态,这一步
res_list.append(res) #是为了取到f1中return的值,才这样取值的,要不就直接加个
#tp.shutdown() 直接就四个四个打印了。所以才加的列表。
tp.shutdown() #主线程等待所有提交给线程池的任务,全部执行完毕 close + join
for r in res_list: #加到列表里是为了能四个四个取值,把所有的返回值地址放到一个列表
print(r.result()) #再循环取值,这样就能四个四个拿值了,要不这样的话,就成了同步取值
print('主线程结束') #了就没效率了
跟进程一样,单词不一样。
import time
from threading import current_thread
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
def f1(n,s):
return n+s
def f2(n):
print('回调函数>>>',n.result())
if __name__ == '__main__':
tp = ThreadPoolExecutor(4)
res = tp.submit(f1,11,12).add_done_callback(f2) #把f1的值,返回给f2 记住方法add_done_callback(f2)
# print(res.result())
进入主题--协程
协程 这个词在官方中是没有定义这样的一个方法的,这个说句大白话,协程 这个方法是
大佬程序员,自己YY出来的。
本节的主题是基于单线程来实现并发,即只用一个主线程(很明显可利用的cpu只有一个)情况下实现并发,为此我们需要先回顾下并发的本质:切换+保存状态
cpu正在运行一个任务,会在两种情况下切走去执行其他的任务(切换由操作系统强制控制),一种情况是该任务发生了阻塞,另外一种情况是该任务计算的时间过长或有一个优先级更高的程序替代了它
协程本质上就是一个线程,以前线程任务的切换是由操作系统控制的,遇到I/O自动切换,现在我们用协程的目的就是较少操作系统切换的开销(开关线程,创建寄存器、堆栈等,在他们之间进行切换等),在我们自己的程序里面来控制任务的切换。
#1 yiled可以保存状态,yield的状态保存与操作系统的保存线程状态很像,但是yield是代码级别控制的,更轻量级 #2 send可以把一个函数的结果传给另外一个函数,以此实现单线程内程序之间的切换
上代码
import gevent
from gevent import monkey;monkey.patch_all() #导入gevent模块中的monkey;monkey.patch_all()
import time #是为了遇到io阻塞直接进行下一任务的方法
import threading
def f1():
print('第一次f1')
# print(threading.current_thread().getName()) #这一步是为了证明两协程是一个线程
#这一步是看名字,两名字都一样,都是同一个线程名字
# gevent.sleep(1) #这一步是相当于time.sleep的功能的一个io阻塞,
time.sleep(2)
print('第二次f1')
def f2():
# print(threading.current_thread().getName())
print('第一次f2')
# gevent.sleep(2)
time.sleep(2)
print('第二次f2')
s = time.time()
g1 = gevent.spawn(f1) #异步提交了f1任务 是异步,记住是异步
g2 = gevent.spawn(f2) #异步提交了f2任务
# g1.join()
# g2.join()
gevent.joinall([g1,g2]) #这是一个小技巧,统一把俩都加上了join,不重要
e = time.time()
print('执行时间:',e-s)
print('主程序任务')
#这就是协程了
二:第一种情况的切换。在任务一遇到io情况下,切到任务二去执行,这样就可以利用任务一阻塞的时间完成任务二的计算,效率的提升就在于此。
协程的本质就是在单线程下,由用户自己控制一个任务遇到io阻塞了就切换另外一个任务去执行,以此来提升效率。为了实现它,我们需要找寻一种可以同时满足以下条件的解决方案
#1. 可以控制多个任务之间的切换,切换之前将任务的状态保存下来,以便重新运行时,可以基于暂停的位置继续执行。 #2. 作为1的补充:可以检测io操作,在遇到io操作的情况下才发生切换
一个进程里面可以开200个线程,一个线程里面可以开500个协程,,,
OK到此,进程,线程,协程都结束了,统称并发编程,下一篇就数据库了,好好学呀大家,
好好学习!天天向上!