有多少个任务就开多少个进程或者线程
什么是池
要在程序开始的时候,还没提交任务先创建几个线程或者进程
放在一个池子里,这就是池
为什么要用池
如果先开好进程/线程,那么有任务之后就可以直接使用这个池中的数据了
并且开好的线程或者进程一直存在在池中,可以被多个任务反复利用,极大减少了开启或者
关闭调度线程/进程的时间开销
池中的线程/进程个数决定了操作系统需要调度的任务个数,控制池中的单位
有利于提高操作系统的效率,减轻操作系统的负担
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
from threading import current_thread
import time
import random
def func(a, b):
print(current_thread().ident, 'start')
time.sleep(random.randint(1, 4))
print(current_thread().ident, 'end')
tp = ThreadPoolExecutor(4) # 控制一次只能执行四个线程
for i in range(10):
tp.submit(func, i, b=i + 1)
实例化 创建池
向池中提交任务,submit传参数(按照位置传,按照关键字传)
进程池概念
import os
from threading import current_thread
import time
import random
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
def func(a, b):
print(os.getpid(), 'start', a, b)
# time.sleep(random.randint(1, 4))
print(os.getpid(), 'end')
if __name__ == '__main__':
tp = ProcessPoolExecutor(4) # 控制一次只能执行四个线程
for i in range(10):
tp.submit(func, i, b=i + 1)
获取任务结果
import os
import time
import random
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
def func(a, b):
print(os.getpid(), 'start', a, b)
time.sleep(random.randint(1, 4))
print(os.getpid(), 'end')
return a * b
if __name__ == '__main__':
tp = ProcessPoolExecutor(4) # 控制一次只能执行四个线程
future_lst = {}
for i in range(10):
ret = tp.submit(func, i, b=i + 1)
future_lst[i] = ret
# print(ret.result()) # Future 未来对象
for key in future_lst: # 同步的
print(key, future_lst[key].result())
map 只适合传递简单的参数,并且是一个可迭代的类型作为参数
import os
import time
import random
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
def func(a):
print(os.getpid(), 'start', a)
time.sleep(random.randint(1, 4))
print(os.getpid(), 'end')
# return a * b
if __name__ == '__main__':
tp = ProcessPoolExecutor(4) # 控制一次只能执行四个线程
ret = tp.map(func, range(20))
for key in ret: # 同步的
print(key)
回调函数
import os
import time, random
# from threading import current_thread
# from concurrent.futures import ThreadPoolExecutor
#
#
# def func(a, b):
# print(current_thread().ident, 'start', a, b)
# time.sleep(random.randint(1, 4))
# print(current_thread().ident, 'end')
# return a * b
#
#
# def print_func(ret):
# print(ret.result())
#
#
# if __name__ == '__main__':
# tp = ThreadPoolExecutor(4)
# future_l = {}
# for i in range(20):
# ret = tp.submit(func, i, b=i + 1)
# ret.add_done_callback(print_func) # 异步阻塞,回调函数给ret对象绑定一个回调函数
# # 等待ret对应的任务有了结果之后立即调用print——func这个函数,立即对结果进行处理
# # 而不是按照顺序接收结果处理结果
# # future_l[i] = ret
# # for key in future_l:
# # print(key, future_l[key].result())
队列机制实现回调机制(回调机制的实现原理)-异步阻塞
```python
import time
import random
import queue
from threading import Thread
def func(q, i):
print('start', i)
time.sleep(random.randint(1, 5))
print('end', i)
q.put(i * (i + 1))
def print_func(q):
print(q.get())
q = queue.Queue()
for i in range(20):
Thread(target=func, args=(q, i)).start()
for i in range(20):
Thread(target=print_func, args=(q,)).start()
回调函数的例子
import requests
import os
from concurrent.futures import ThreadPoolExecutor
def get_page(url): # 访问网页,获取网页源代码线程池中的线程来操作
print('<进程%s> get%s' % (os.getppid(), url))
response = requests.get(url)
if response.status_code == 200:
return {'url': url, 'text': response.text}
def parse_page(res): # 获取到字典结果后,计算网页源码的长度,把https://www.baidu.com:192974729
# 写到文件里,线程任务执行完毕之后绑定回调函数
res = res.result()
print('<进程%s> parse %s' % (os.getpid(), res['url']))
parse_res = 'url:<%s> size:[%s]\n' % (res['url'], len(res['text']))
with open('db.txt', 'a') as f:
f.write(parse_res)
if __name__ == '__main__':
urls = [
'https://www.baidu.com',
'https://www.python.org',
'https://www.openstack.org',
]
# 线程池开启
tp = ThreadPoolExecutor(4)
# 循环urls列表
for url in urls:
# 得到一个futures对象=把每一个url提交一个get_page任务
ret = tp.submit(get_page, url)
# 给feature对象绑定一个parse_page回调函数
ret.add_done_callback(parse_page)
不用回调函数:
按照顺序获取网页,
也只能按顺序写
用了回调函数
按照顺序获取网页,哪一个网页先返回结果,就先执行那个网页对应的parserpage(回调函数)
要求
会起池,会提交任务
会获取返回值,会用回调函数
进程池(高计算的场景,没有io(没有文件操作\没有数据库操作\没有网络操作\没有input)):
>cpu_count*1 <cpu_count*2
线程池(一般根据io的比例定制):cpu_count*5