-
前边套接字通信服务端多任务并发,套接字通信是I/O密集型的任务,用多线程。主线程建连接,每建一个连接就起一个线程。 彼此任务不再影响。
但这种方式会有服务端无限开线程的问题。所以要限制并发的个数,使用pool的概念。 -
开线程池,池的大小设置:设为机器可以承受的范围。线程池是一种折中的方案,其实比不开池效率低,但保证了安全机器不会崩。线程池应用于问题规模大,要开许多线程,多到机器无法承受的地步,这时使用线程池让机器保持健康的状态。问题规模持续增大,线程池反而会限制效率。
程序的效率问题集中在I/O问题上。综上线程池的应用:线程的数量规模小,但是超过了机器的承受范围,从而设置线程池保证机器的一种健康状态。egMysql的支持并发连接 -
ThreadPoolExecutor,ProcessExecutor 的用法完全一样,因此
先明白什么时候用池(上文方法),再明白用什么池(线程与进程的区别) -
提交任务的两种方式
同步调用:提交完任务后就在原地等待,等待任务执行完毕,拿到任务的返回值,才能继续执行下一行代码,导致程序串行执行。其好处可以在原地立刻拿到结果,那到结果就立刻处理。异步调用:提交完任务后,不在原地等待,程序并发执行
池提交任务是异步调用,将所有任务都丢到池中,能够执行的数量取决于池的大小,多余的额在等在,池有空闲就可以立刻加进去一个任务执行。
异步调用,结果去那?什么时候运行完就拿到结果了,但是无法直接拿到结果,为了处理结果,就在任务函数计算出结果之后调用处理数据的函数。但是两个函数耦合在一起了。所以通常采用异步调用与回调机制结合的方法。
异步调用+回调机制:提交完任务后,不在原地等待,任务一旦执行完毕就会触发回调函数的执行,回调函数会拿到任务的结果。 -
阻塞与非阻塞
进程的运行其实是线程的运行,进程的运行状态也可以搬到线程身上
阻塞不等于同步调用!同步调用等待是因为等待程序执行完毕拿结果,而阻塞是因为遇到I/O问题,被剥夺了CPU的执行权限。
同步调用可能遇到或者遇不到阻塞 -
进程池
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor #这两个用法一样,这两个比Pool的封装程度更高,Pool中有方法apply,有同步调用但是没用。
import time,random,os
def task(n):
print('%s is running' %os.getpid())
time.sleep(random.randint(1,3))
#res=n**2 #耦合度太高
#handle(res)
return n
def handle(res):
res=res.result() #任务执行完才回触发执行这个回调函数
print('处理任务%s的结果' %res)
if __name__=='__main__':
pool=ProcessPoolExecutor(4) #进程池,默认是主机的核数,进程池的大小通常不要超过CPU的两倍。线程池的大小需要调到计算机可以承受的范围
#进程池帮忙起进程
for i in range(13): #13个任务会全部丢到进程池
pool.submit(task,i).add_done_callback(handle) #提交任务时起进程,异步调用,并发,后边的意识是每个任务得到结果后再使用回调函数去处理运行的结果,#但是handle函数收到的并不是任务的结果,而是任务对象本身,因此handle函数内要调用.result()将结果拿到
#pool.submit(task,i).result() 用这句就使得程序串行了,因为它要原地取结果 实现了同步调用,如果需要立刻拿到结果去处理就使用这个方法
pool.shutdown(wait=True) #意为将进程join,有两个功能
#指的是主进程要等待进程池中的所有进程执行完才执行后续代码
#这句话之后,进程池就不允许再提交新任务了。
结果:
2528 is running
5816 is running
3512 is running
11168 is running
3512 is running
处理任务2的结果
处理任务0的结果
2528 is running
5816 is running
处理任务1的结果
处理任务5的结果
2528 is running
5816 is running
处理任务6的结果
3512 is running
处理任务4的结果
11168 is running
处理任务3的结果
5816 is running
处理任务8的结果
11168 is running
处理任务10的结果
处理任务7的结果
处理任务11的结果
处理任务9的结果
处理任务12的结果
- 线程池
#线程池
#爬虫:模拟浏览器向目标站点获取网页拿到数据并且通过正则表达式提取想要的数据
from concurrent.futures import ThreadPoolExecutor
import requests #模拟浏览器提交HTTP请求 I/O密集型
import time
from threading import current_thread
def get(url):
print('%s GET %s' %(current_thread().getName(),url))
response=requests.get(url)
time.sleep(2)
if response.status_code==200: #表示请求成功
return{'url':url,'content':response.text}
def parse(res):
res=res.result()
print('parse:[%s] res:[%s]' %(res['url'],len(res['content'])))
if __name__=='__main__':
pool=ThreadPoolExecutor(4) #默认个数是CPU个数的5倍
urls=[
'https://www.baidu.com',
'https://www.python.org',
'https://www.openstack.org',
'https://www.qq.com',
'https://www.openstack.org',
'https://www.openstack.org',
'https://www.openstack.org',
'https://www.openstack.org',
'https://www.openstack.org',
'https://www.openstack.org',
'https://www.openstack.org',
'https://www.openstack.org',
'https://www.openstack.org',
'https://www.openstack.org',
'https://www.openstack.org',
]
for url in urls:
pool.submit(get,url).add_done_callback(parse)
结果:
ThreadPoolExecutor-0_0 GET https://www.baidu.com
ThreadPoolExecutor-0_1 GET https://www.python.org
ThreadPoolExecutor-0_2 GET https://www.openstack.org
ThreadPoolExecutor-0_3 GET https://www.qq.com
parse:[https://www.baidu.com] res:[2443]
ThreadPoolExecutor-0_0 GET https://www.openstack.org
parse:[https://www.qq.com] res:[221808]
ThreadPoolExecutor-0_3 GET https://www.openstack.org
parse:[https://www.openstack.org] res:[56635]
ThreadPoolExecutor-0_0 GET https://www.openstack.org
parse:[https://www.openstack.org] res:[56635]
ThreadPoolExecutor-0_3 GET https://www.openstack.org
parse:[https://www.python.org] res:[48720]
ThreadPoolExecutor-0_1 GET https://www.openstack.org
parse:[https://www.openstack.org] res:[56635]
ThreadPoolExecutor-0_0 GET https://www.openstack.org
parse:[https://www.openstack.org] res:[56635]
ThreadPoolExecutor-0_2 GET https://www.openstack.org
parse:[https://www.openstack.org] res:[56635]
ThreadPoolExecutor-0_3 GET https://www.openstack.org
parse:[https://www.openstack.org] res:[56635]
ThreadPoolExecutor-0_1 GET https://www.openstack.org
parse:[https://www.openstack.org] res:[56635]
ThreadPoolExecutor-0_2 GET https://www.openstack.org
parse:[https://www.openstack.org] res:[56635]
ThreadPoolExecutor-0_3 GET https://www.openstack.org
parse:[https://www.openstack.org] res:[56635]
parse:[https://www.openstack.org] res:[56635]
parse:[https://www.openstack.org] res:[56635]
parse:[https://www.openstack.org] res:[56635]
不论是进程还是线程,都是先造好,再向其内填任务