# 主线程等待所有任务执行完成import time
from concurrent.futures import ThreadPoolExecutor
pool = ThreadPoolExecutor(3)deftask(name):print('%s 开始'%name)
time.sleep(1)print('%s 结束'%name)if __name__ =='__main__':for i inrange(20):
pool.submit(task,'屌丝%s'%i)# 放到for外面
pool.shutdown(wait=True)# 等待所有任务完成,并且把池关闭# pool.submit(task,'sddd') errorprint('主')# 立马执行,20个线程都执行完了,再执行
2. 定时器
# 多长时间之后执行一个任务from threading import Timer
deftask(name):print('我是大帅比--%s'%name)if __name__ =='__main__':# t = Timer(2,task,args=('lqz',)) # 本质是开了个线程,延迟两秒执行
t = Timer(2,task,kwargs={
'name':'lqz'})
t.start()
3. 协程介绍
# 进程 线程 协程# 协程是为了实现单线程下的并发 属于线程下 又称微线程 协程是线程的具体实现# 协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的# 协程要解决的问题: 保存状态 + 切换# yield: 生成器 只要函数中有yield关键字 这个函数就是生成器 通过yield可以实现保存状态 + 切换'''
注意:
1. python的线程属于内核级别的,即由操作系统控制调度
(如单线程遇到io或执行时间过长就会被迫交出cpu执行权限,切换其他线程运行)
2. 单线程内开启协程,一旦遇到io,就会从应用程序级别(而非操作系统)控制切换,
以此来提升效率(非io操作的切换与效率无关)
'''import time
# 串行执行deffunc1():for i inrange(1000000):
i +=1deffunc2():for i inrange(1000000):
i +=1if __name__ =='__main__':
ctime = time.time()
func1()
func2()print(time.time()- ctime)# 7s多# 通过yield 实现保存状态加切换 (自定义的切换,并不是遇到io才切换,所以它并不能节约时间)# 单纯的切换 不但不会提高效率 反而会降低效率deffunc1():for i inrange(1000000):
i +=1yielddeffunc2():
g = func1()# 先执行一下func1for i inrange(1000000):
i +=1next(g)# 回到func1执行if __name__ =='__main__':
ctime = time.time()
func2()print(time.time()- ctime)# 14.764776706695557# 协程并不是真正存在的某个东西,而是程序员臆想出来的# 程序员控制,不让自己的程序遇到io,看上去就实现并发了'''
协程优点:
协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级
单线程内就可以实现并发的效果,最大限度的利用了cpu
协程缺点:
协程的本质是在单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程
每个线程内开启协程
协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程
协程特点:
必须在只有一个单线程里实现并发
修改共享数据不需要加锁
用户程序里自己保持多个控制流的上下文栈 (需要保持状态)
一个协程遇到io操作自动切换到其他协程
(如何实现检测io,yield.greenlet都无法实现,就用到了gevent模块)
'''
1. 用协程改写生产者消费者模型
# 生产者defprodecer():for i inrange(100):yield i
# 消费者defconsumer(gen):for i inrange(10):print(next(gen))# 初始化生成器函数 -> 生成器
gen = prodecer()
consumer(gen)
consumer(gen)
consumer(gen)