本文是关于python高性能多任务编程的总结与概括。有很多细节没有详述,有需要了解更多的可以关注我,看看我前几期的文章。
一、选择操作系统
高性能编程需要选择合适的操作系统。因为windows下创建子进程是通过重新加载py程序文件;而linux和mac创建子进程是通过复制父进程的内存空间,所以在windows平台下运行多进程python程序的效率远不如在linux和mac平台下运行。
二、分析任务类型
高性能编程有三种方式:
-
多进程,追求高运算:
当任务类型是计算多IO少时,建议使用进程池的方式来编程。进程池中进程的数量一般推荐为CPU内核数的一至两倍。多进程程序运行时是并行执行。
常见任务:科学计算、数据分析等。
注意:创建进程开销极大,进程之间数据隔离,多进程存在数据不安全的情况,可规避所有IO。
-
多线程,运用最多:
当任务类型是IO多计算少时,建议使用线程池的方式来编程。线程池中线程的数量一般推荐为CPU内核数的四到五倍,至多不超过十倍。多线程程序运行时是并发执行(由于GIL锁造成python多线程执行时不能并行执行)。
常见任务:爬虫等。
注意:创建线程开销小,线程之间数据共享,多线程存在数据不安全的情况,可规避所有IO。
-
协程,追求高并发:
当任务类型是IO多计算少且需要大幅度提高并发数时,建议使用协程或多进程+多线程+协程的方式。
常见任务:爬虫、网络服务、web服务器等。
注意:创建协程开销极小,线程之间数据共享,多线程不存在数据不安全的情况,但只能规避IO。
三、Python解释器类型
目前大家用的最多的是CPython,它是用C语言实现的Python解释器,也是官方的并且是使用最广泛的Python解释器,它的软件包和兼容性最佳,CPython最新版本是3.9.1。官网是https://www.python.org/ 。
如果要追求高性能的话目前值得推荐的解释器是PyPy,PyPy提供了JIT编译器和沙盒功能,因此运行速度比CPython要快,它的软件包和兼容性不如CPython,Pypy最新版本是3.7。官网是https://www.pypy.org/ 。
四、多任务编程注意事项
多任务编程使用线程池的方式最多,要注意python线程编程的一个缺陷,即不能在线程外部关闭超时的线程。所以需要在任务内部对超时情况做处理。下面写一个简单的模拟爬虫案例:
代码:
from concurrent.futures import ThreadPoolExecutor
import requests
url = [('csdn', 'https://www.csdn.net/'), ('cnblogs', 'https://www.cnblogs.com/'),
('baidu', 'https://www.baidu.com/'), ('toutiao', 'https://www.toutiao.com/'),
('google', 'https://www.google.com/')]
def get(site):
try:
r = requests.get(site[1], timeout=5)
except requests.exceptions.ConnectTimeout:
return site[0], '超时'
return site[0], '正常'
if __name__ == "__main__":
p_list = []
with ThreadPoolExecutor(max_workers=5) as e:
ret = e.map(get, url)
for i in ret:
print(f'网站“{i[0]}”下载情况是“{i[1]}”')
输出:
网站“csdn”下载情况是“正常”
网站“cnblogs”下载情况是“正常”
网站“baidu”下载情况是“正常”
网站“toutiao”下载情况是“正常”
网站“google”下载情况是“超时”
说明:
我们需要多任务执行的是get函数,它用来下载网页。在get中要对io操作进行超时预判,即超过5秒获取不到结果的网站放弃爬取。假设不设超时预判会导致线程一直反复爬取但一直获取不到数据而陷入死循环,极其影响整体影响效率。
总结:
因为python多线程无法从外部结束超时的线程,所以在多任务编程中要在函数内部对可能存在异常的任务做超时判断及处理。