python 常用于算法,以及数据分析,对于从常混在后端编码的我,用的最长是Java,但上述两个场景,业务辗转,技不压身,虽然业务完成,一直没有从基础掌握,感觉还是门外汉,今天梳理一下。 GIL是什么?一开就是英文缩写,global interpreter lock (全局解释器锁),全局互斥锁,同一个时刻仅有一个线程执行python 字节码,偏向IO密集型场景(网络请求、文件读写、DB操作)。Cpython内存管理,引用计数是不安全的。如何规避不是真正意义的并发? CPU密集型:采取多进程 multiprocessing,C/C++扩展/Cpython(Numpy 就是典型); IO密集型:使用 asyncio协程(单线程,IO多路复用,绕开线程调度问题,超高并发IO密集型)协程:用户态切换,极轻量,单线程,完全自己控制调度,无系统调用开销 python 内存管理相对于JAVA 来说,要容易得些;采用引用计数,无法解决循环引用;为何解决循环引用,靠标记清除以及分代回收,对象按存活时间分0、1、2三代。新对象放入第0代,扫描频率最高,存活越久,扫描越少,提升效率。内存泄漏场景:循环引用且代_del_方法,GC无法回收,全局列表/缓存 无限增长,闭包、装饰器意外持有大对象引用。第三方库未正确释放句柄/连接,多进程/多线程共享资源未释放。from functools import partialdef create_fns(): fns= []for i in [1,2,3]:def func(i):return i f = partial(func, i=i) fns.append(f)return fnsif __name__=="__main__": f1, f2, f3 =create_fns()# 闭包延迟绑定:函数定义时不取值,函数调用时才去找变量当时的值。 破解通过参数默认值绑定或 functools 立即绑定print(f1(), f2(), f3()) # 输出:3 3 3
asyncio 为什么不能让所有的库异步?异步不等于非阻塞I/O。大多数库内部做的CPU计算或同步阻塞I/O,它们会卡死事件。事件循环的心脏是单线程,同步I/O库,如requests,调用requests.get(url) 这个函数内部执行一个系统调用,该调用让当前线程进入阻塞。在asyncio下,会导致事件循环完全停止。为什么不能改造所有的库?C扩展的枷锁,如numpy,mysqlclient底层是C语言写的,GIL和事件循环无法强制打开它。生态基因,如psycopg2 生下来就是同步阻塞的;协议中枷锁,文件系统的操作(open,write,read)在大多数操作系统上没有真正的异步接口。所谓的文件异步读写,本质上还是放到线程池里做阻塞操作。I/O密集型,使用原生异步库,aiohttp代替 requests;CPU密集型,使用asyncio.to_thread 将任务踢到任务池。或使用multiprocessing踢到进程池。避免阻塞事件循环。 背压:TCP层面,当接受缓存区满了,操作系统会缩小TCP窗口,甚至发送零窗口通告,让发送方降速,这就是最底层的背压。__new__ 和 __init__ 的区别?创建实例,分配内存,一个是初始化实例 在这里学到301和302的差异,301永久重定向,低于浏览器缓存不利于统计,302临时重定向,便于追踪点击数 base62编码(字符集 0-9,a-z,A-Z)=62字符