一、线程池和进程池的shutdown
作用:主线程/线程等待所有任务执行完成后,再将池子关闭,关闭以后就不能再提交任务了
代码演示:
from concurrent.futures import ThreadPoolExecutor
import time
pool = ThreadPoolExecutor(3)
def task(name):
print(f'{name}开始了')
time.sleep(2)
print(f'{name}结束了')
if __name__ == '__main__':
for i in range(20):
pool.submit(task,f'线程{i}')
# 放到for循环外面
pool.shutdown(wait=True) # 等待所有任务结束,并且把池子关闭
# 关闭以后不能再提交任务
# pool.submit(task, 'aaa')
print('主') # 子线程的任务全部执行结束了就立马执行
二、定时器
作用:设定多长时间之后执行一个任务
使用Timer类实现,Timer类是Thread类的子类
代码演示:
from threading import Timer
def task(name):
print(f'{name}:太难了,我怎么可以这么帅!')
if __name__ == '__main__':
t = Timer(2, task, args=('cc',)) # 本质是开一个线程,延迟2秒执行
t.start()
三、协程介绍
进程、线程、协程
-协程是为了实现单线程下的并发,又称微线程
协程是一种用户态的轻量级线程,也就是说协程是由用户程序自己控制调度的。
本质上是在单线程下,由用户自己控制一个任务遇到IO阻塞了就切换另一个任务去运行,以此来提升效率。
-协程要做的事情:
1、可以控制多个任务之间的切换,切换之前将任务的状态保存下来,以便重新运行时,可以基于暂停的位置继续运行
2、可以检测IO操作,遇到IO操作的情况下才发生切换
-协程并不是真实存在的事物,而是程序员臆想出来的
程序员控制,不让自己的程序遇到IO,看上去,就实现并发了
-优点:
① 协程的切换开销更小,属于程序级别的切换,操作体统完全感知不到,因而更加轻量级
② 单线程内就可以实现并发地效果,最大限度地利用CPU
-缺点:
① 协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启多个协程
② 协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程
总结:
1、必须在只有一个单线程里实现并发
2、修改共享数据不需要加锁
3、用户程序里袭击保存多个控制流的上下文线
4、一个协程遇到IO操作自动切换到其他协程(需要用到gevent模块的select机制,使用yield、greenlet无法实现)
代码演示yield关键字实现的协程:
import time
def func1():
for i in range(10000000):
i += 1
yield
def func2(): # 生成器
g = func1()
for i in range(10000000):
i += 1
next(g)
if __name__ == '__main__':
ctime = time.time()
func2()
print(time.time() - ctime)
四、greenlet模块
应用场景:
如果在单个线程内有20个任务,要想实现多个任务之间切换,使用yield生成器就会过于麻烦
而使用greenlet模块就可以非常简单的实现
ps: greentlet模块是第三方模块,需要pip安装
代码示例:
from greenlet import greenlet
import time
def eat(name):
print(f'{name}吃了一口')
p.switch('cc')
print(f'{name}又吃了一口')
p.switch()
def play(name):
print(f'{name}玩了一会')
e.switch()
time.sleep(1)
print(f'{name}又玩了一会')
if __name__ == '__main__':
e = greenlet(eat)
p = greenlet(play)
e.switch('cc')
**总结:**greenlet模块是一个初级模块,遇到IO操作不会切换,要想处理IO操作时的切换,需要使用基于它写的gevent模块
五、gevent模块
gevent模块是基于greenlet模块写的一个第三方模块,实现了遇见IO自动切换
可以轻松通过gevent实现并发同步或者一部编程,在gevent中用到的是greenlet
# 用法:
g1 = gevent.spawn(func,1,2,3,x=4,y=5) # 创建一个协程对象g1,spawn括号内的第一个参数是函数名,后面可以是位置实参或者关键字实参,都是传给func函数的
g2 = gevnet.spawn(func2)
g1.join() # 等待g1结束
g2.join() # 等待g2结束
# 或者上述两步合成一步: gevent.joinall([g1, g2])
g1.value # 拿到func1的返回值
代码演示:
from gevent import monkey;monkey.patch_all() # 使用猴子补丁,这一句必须写
import gevent
import time
def eat(name):
print(f'{name}吃了一口')
time.sleep(1) # IO操作,使用猴子补丁将原来的gevent.sleep()替换了
print(f'{name}又吃了一口')
def play(name):
print(f'{name}玩了一会')
time.sleep(2)
print(f'{name}又玩了一会')
if __name__ = '__main__':
ctime = time.time()
e = gevent.spawn(eat, 'cc')
p = gevent.spawn(play, 'cc')
e.join() # 等待e执行完成
p.join()
print('主')
print(time.time() - ctime)
六、猴子补丁
class Monkey():
def play(self):
print('猴子在玩')
class Dog():
def play(self):
print('狗子在玩')
m = Monkey()
m.play = Dog().play
m.play() # 使用猴子补丁完成替换
使用ujson模块替换json模块:
import json
import ujson
def monkey_patch_json():
json.__name__ = 'ujson'
json.dumps = ujson.dumps
json.loads = ujson.loads
monkey_patch_ujson()
八、asyncio模块
asyncio模块是官方支持协程的库
使用方法:
-event_loop 时间循环:程序开启一个无限循环,程序员会把一些函数注册到事件循环上。当满足事件发生的时候,调用相应的协程函数
-coroutine 协程:协程对象,指一个使用async关键字定义的函数,它的调用不会立即执行函数,而是会返回一个协程对象。协程对象需要注册到事件循环,有事件循环调用
-task 任务:一个协程对象就是一个原生可以挂起的函数,任务就是对协程进一步封装,其中包含任务的各种状态
-future:代表将来执行或没有执行的任务的结果。它和task没有本质的区别
-async/await关键字:python3.5用于定义协程的关键字,async用于定义一个协程,await用于挂起阻塞的异步调用接口
python3.5之前的版本
import time
import asyncio
@asyncio.coroutine
def task():
print('开始')
yield from asyncio.sleep(1) # 模拟IO
print('结束')
loop = asyncio.get_event_loop() # 获取一个时间循环对象
# 协程函数加括号并不会真正的执行,它需要提交给loop,让loop循环着去执行
# 协程函数列表
ctime = time.time()
t = [task(), task()]
lop.run_until_comlete(asyncio.wait(t))
loop.close()
print(time.time() - ctime)
新版本使用示例:
import time
import asyncio
from threading import current_thread
async def task1(): # 表示定义的是一个协程函数,等同于python3.5之前的装饰器
print('开始')
print(current_thread().name)
await asyncio.sleep(3) # await等同于之前的yield from
print('结束')
async def task2():
print('开始')
print(current_thread().name)
await asyncio.sleep(2) # await等同于之前的yield from
print('结束')
loop = asyncio.get_event_loop()
ctime = time.time()
t = [task1(),task2()]
loop.run_until_complete(asyncio.wait(t))
loop.close()
print(time.time() - ctime)