![](https://img-blog.csdnimg.cn/20201014180756927.png?x-oss-process=image/resize,m_fixed,h_64,w_64)
python并发与并行
文章平均质量分 86
bug404_
女程序猿,计算机视觉方向,主要关注transformer、视觉、深度学习框架、模型压缩和部署相关的内容。
展开
-
python并发与并行(十二) ———— 考虑用concurrent.futures实现真正的并行计算
Python里面针对C扩展而设计的那些API,有详细的文档可以参考,所以这是个很好的备选方案。所谓数据利用度较高(high-leverage),这里指任务所使用的原始材料以及最终所给出的结果数据量都很小,因此上级进程与子进程之间只需要互传很少的信息就行,然而在把原始材料加工成最终产品的过程中,却需要做大量运算。问题是,在大多数情况下,我们不能只把整个程序里的一小段代码移植到C语言,因为程序的性能之所以缓慢,通常是由多个因素造成的,而不是说只要消除了其中某一个主要因素,整个程序的性能就会大幅提升。原创 2024-08-28 22:22:20 · 713 阅读 · 0 评论 -
python并发与并行(十一) ———— 让asyncio的事件循环保持畅通,以便进一步提升程序的响应能力
前一篇blog说明了怎样把采用线程所实现的项目逐步迁移到asyncio方案上面。迁移后的run_tasks协程,可以将多份输入文件通过tail_async协程正确地合并成一份输出文件。但这样写有个大问题,就是针对输出文件所做的open、close以及write操作,全都要放在主线程中执行,而这些操作又需要在程序所处的操作系统执行系统调用,这些调用可能会让事件循环阻塞很长一段时间,导致其他协程没办法推进。这会降低程序的总体响应能力,而且会增加延迟,对于高并发服务器来说,这个问题尤其严重。原创 2024-08-28 22:21:43 · 755 阅读 · 0 评论 -
python并发与并行(十) ———— 结合线程与协程,将代码顺利迁移到asyncio
但是,那个函数的任务本身就是执行许多项阻塞式的文件I/O操作,所以好像没必要移植,因为我们在判断是否需要移植时,应该考虑到,这样做会不会让代码变得难懂,会不会降低程序的效率。asyncio模块的事件循环提供了一个返回awaitable对象的run_in_executor方法,它能够使协程把同步函数放在线程池执行器(ThreadPoolExecutor)里面执行,让我们可以顺利地将采用线程方案所实现的项目,从上至下地迁移到asyncio方案。反过来说,这条线程要是退出了,那就意味着有人把那份文件的句柄关了。原创 2024-08-28 22:21:09 · 470 阅读 · 0 评论 -
python并发与并行(九) ———— 用asyncio改写通过线程实现的IO
知道了协程的好处之后,我们可能就想把现有项目之中的代码全都改用协程来写,于是有人就担心,这样修改起来,工作量会不会比较大呢?所幸Python已经将异步执行功能很好地集成到语言里面了,所以我们很容易就能把采用线程实现的阻塞式I/O操作转化为采用协程实现的异步I/O操作。在这里我们要补充下线程和协程的区别,以及他们在执行阻塞式IO和异步IO上的区别。在计算机编程中,I/O(输入/输出)操作通常涉及等待外部事件完成,如磁盘读写、网络通信等。原创 2024-08-28 22:20:36 · 223 阅读 · 0 评论 -
python并发与并行(八) ———— 用协程实现高并发的I/O
协程与线程的区别在于,它不会把这个函数从头到尾执行完,而是每遇到一个await表达式,就暂停一次,下次继续执行的时候,它会先等待await所针对的那项awaitable操作有了结果(那项操作是用async函数表示的),然后再推进到下一个await表达式那里(这跟生成器函数的运作方式有点像,那种函数也是一遇到yield就暂停)。这种强大的机制是通过事件循环(event loop)打造的,只要把相关的函数写对,这种循环就可以穿插着执行许多个这样的函数,并且执行得相当快,从而高效地完成并发式的I/O任务。原创 2024-08-28 22:20:03 · 812 阅读 · 0 评论 -
python并发与并行(七) ———— 如果必须用线程做并发,那就考虑通过ThreadPoolExecutor实现
这次在把游戏推进到下一代的时候,我们不针对每个单元格启动新的Thread实例,而是把推进每个单元格状态的那个函数与必要的参数提交给ThreadPoolExecutor,让执行器自己安排线程去执行这些状态更新任务,这样就实现了fan-out(分派)。另外,线程池里的最大线程数可以通过max_workers参数手工指定,这样能把线程数量限制在一定范围内,而不像最早的那个方案那样,每执行一项I/O操作,就启动一条线程,那样会导致内存用量激增。我们把之前的代码拿过来进行更改。比之前的结果都要离谱。原创 2024-08-28 22:19:25 · 414 阅读 · 0 评论 -
python并发与并行(六) ———— 正确的重构代码,以便用Queue做并发
在前面“python并发与并行(五.2) ———— 不要在每次fan-out时都新建一批Thread实例”里面,大家看到,每次都手工创建一批线程并平行地执行I/O任务是有很多缺点的。 这一条要介绍另一种方案,也就是用内置的queue模块里的Queue类实现多线程管道。Queue方案的总思路是:在推进游戏时,不像原来那样,每推进一代,就新建一批线程来推进相应的单元格,而是可以提前创建数量固定的一组工作线程,令这组线程平行地处理当前这批I/O任务,并在处理完之后,继续等待下一批任务,这样就不会消耗那么多资源了,原创 2024-08-28 22:19:01 · 843 阅读 · 0 评论 -
python并发与并行(五.2) ———— 不要在每次fan-out时都新建一批Thread实例
这会让多线程版本的代码理解起来比较困难,因为早前的单线程版是按照先后顺序实现的,不需要有专门的协调机制,所以读起来比现在的版本好懂。第二,线程占用的内存比较多,每条线程大约占8MB。第三,线程的开销比较大。每次都手工创建一批线程,是有很多缺点的,例如:创建并运行大量线程时的开销比较大,每条线程的内存占用量比较多,而且还必须采用Lock等机制来协调这些线程。每次都手工创建一批线程,是有很多缺点的,例如:创建并运行大量线程时的开销比较大,每条线程的内存占用量比较多,而且还必须采用Lock等机制来协调这些线程。原创 2024-08-28 22:17:33 · 888 阅读 · 0 评论 -
python并发与并行(五.1) ———— 不要在每次fan-out时都新建一批Thread实例
这个程序,在单机单线程的环境下,是没有问题的。get指的是当前这代网格(grid)之中的get方法,而set指的则是下一代网格(next_grid)的set方法,只有这样,才能让每一个单元格都按照现在的情况分别演化到下一轮,而不会让先演化的单元格影响其他单元格的迁移结果,这对于游戏正常运行是很重要的。与game_logic`,这个函数负责根据坐标查出单元格当前的状态,然后统计周边总共有多少个存活的单元格,接下来根据当前状态与存活的邻居数量判断本单元格在下一轮的状态,最后,更新单元格的状态。原创 2024-08-28 22:16:27 · 569 阅读 · 0 评论 -
python并发与并行(四) ———— 用queue来协调多个线程之间的工作进度
Python程序如果要同时执行多项任务,而这些任务又分别针对同一种产品的不同环节,那么就有可能得在它们之间进行协调。比较有用的一种协调方式是把函数拼接成管道。这样的管道与生产线比较像。它可以按先后顺序划分成几个阶段,每个阶段都由相应的函数负责。程序会把未经加工的原料放在生产线(也就是管道)的起点,而那些函数,则分别关注着自己所负责的这一段,只要有产品来到当前环节,它就对这件产品做出相应的加工处理。如果所有函数都不会再收到有待加工的产品,那么整条生产线就可以关停。原创 2024-08-28 22:15:45 · 906 阅读 · 0 评论 -
python并发与并行(三) ———— 利用Lock防止多个线程争用同一份数据
所以,多个线程同时访问同一个对象是很危险的。这三个步骤本来应该一次执行完才对,但是Python系统有可能在任意两步之间,把当前这条线程切换走,这就导致这条线程在切换回来后,看到的是个已经过时的value值,它把这个过时的值通过setattr赋给Counter对象的count属性,从而使统计出来的样本总数偏小。通过这样的锁,我们可以确保多条线程有秩序地访问Counter类的count属性,使得该属性不会遭到破坏,因为线程必须先获取到这把锁,然后才能操纵count,而每次最多只能有一条线程获得该锁。原创 2024-08-28 22:15:12 · 661 阅读 · 0 评论 -
python并发与并行(二) ———— 用线程执行阻塞式IO,但不要用它做并行计算
与依次执行系统调用的那种写法相比,这种写法的速度几乎能达到原来的5倍。这说明,尽管那5条线程依然受GIL制约,但它们所发起的系统调用是可以各自向前执行的。GIL只不过是让Python内部的代码无法平行推进而已,至于系统调用,则不会受到影响,因为Python线程在即将执行系统调用时,会释放GIL,待完成调用之后,才会重新获取它。GIL只不过是让Python内部的代码无法平行推进而已,至于系统调用,则不会受到影响,因为Python线程在即将执行系统调用时,会释放GIL,待完成调用之后,才会重新获取它。原创 2024-08-28 22:14:40 · 242 阅读 · 0 评论 -
python并发与并行(一) ———— 用subprocess管理子进程
如果你只需要简单地运行一个命令并等待其完成,同时获取其输出,那么 subprocess.run 是更好的选择。如果你需要更复杂的进程控制(例如,与子进程进行交互、在子进程运行时执行其他操作、或处理多个并行的子进程),那么 subprocess.Popen 将是更合适的选择。把子进程从父进程中剥离,可以让程序平行地运行多条子进程。例如,我们可以像下面这样,先把需要运行的这些子进程用Popen启动起来。原创 2024-08-28 22:14:09 · 689 阅读 · 0 评论