22 | 并发编程之Asyncio
该思维导图由 AI 生成,仅供参考
什么是 Asyncio
Sync VS Async
- 深入了解
- 翻译
- 解释
- 总结
Python并发编程有多种实现方式,其中之一是Asyncio。与多线程相比,Asyncio能够更有效地处理I/O操作,避免了线程切换的损耗和可能出现的race condition情况。Asyncio采用单线程和特殊的future对象来实现多个任务的并发执行,通过event loop控制任务的状态和执行顺序。在代码实现上,Asyncio使用async和await关键字来标识非阻塞操作,以及提供了一系列的函数来管理任务的执行。虽然Asyncio在处理I/O密集型任务时表现出色,但在实际应用中需要注意兼容性问题和任务调度的细节。总体而言,对于I/O密集型且I/O操作较慢的情况,使用Asyncio更为合适;而对于I/O操作较快的情况,多线程可能更适合。对于CPU密集型任务,则需要使用多进程来提高程序运行效率。 Asyncio是单线程的,但其内部event loop的机制,可以让它并发地运行多个不同的任务,并且比多线程享有更大的自主控制权。在I/O操作heavy的场景下,Asyncio比多线程的运行效率更高,因为Asyncio内部任务切换的损耗远比线程切换的损耗要小,并且Asyncio可以开启的任务数量也比多线程中的线程数量多得多。然而,使用Asyncio需要特定第三方库的支持,而在I/O操作很快的情况下,多线程也能有效地解决问题。 另外,文章还提到了并行编程(multi-processing),适用于CPU heavy的场景。为了比较程序的耗时,文章提出了一个需求:输入一个列表,对于列表中的每个元素,计算0到这个元素的所有整数的平方和。通过查阅资料,可以写出该需求的多进程版本,并进行程序耗时的比较。 总的来说,本文介绍了Asyncio的原理和用法,并比较了Asyncio和多线程各自的优缺点,同时提到了多进程适用于CPU heavy的场景。这些内容对于读者快速了解并发编程的不同实现方式以及其适用场景具有重要参考价值。
《Python 核心技术与实战》,新⼈⾸单¥59
全部留言(72)
- 最新
- 精选
- Jingxiao置顶思考题答案: import multiprocessing import time def cpu_bound(number): return sum(i * i for i in range(number)) def find_sums(numbers): with multiprocessing.Pool() as pool: pool.map(cpu_bound, numbers) if __name__ == "__main__": numbers = [10000000 + x for x in range(20)] start_time = time.time() find_sums(numbers) duration = time.time() - start_time print(f"Duration {duration} seconds")2019-07-02729
- 建强上网查询资料后,初步了解了多进程的一些知识,按照资料中的方法简单改写了一下程序,由于多进程方式时,不知什么原因,cpu_bound函数不能实时输出,所以就把cpu_bound改为返回字符串形式的结果,等所有的数计算完成后,再一并输出结果 ,程序中常规执行和多进程两种方式都有,并作了对比后发现,常规执行用时约23秒,多进程用时约6秒,两者相差4倍,程序如下,不足处请老师指正: #多进程演示 import multiprocessing import time def cpu_bound(number): return 'sum({}^2)={}'.format(number,sum(i * i for i in range(number))) def calculate_sums(numbers): results = [] print('-'*10+'串行执行开始:'+'-'*10) for number in numbers: results.append(cpu_bound(number)) print('-'*10+'串行执行结束,结果如下:'+'-'*10) for res in results: print(res) def multicalculate_sums(numbers): #创建有4个进程的进程池 pool = multiprocessing.Pool(processes=4) results = [] print('-'*10+'多进程执行开始:'+'-'*10) #为每一个需要计算的元素创建一个进程 for number in numbers: results.append(pool.apply_async(cpu_bound, (number,))) pool.close() #关闭进程池,不能往进程池添加进程 pool.join() #等待进程池中的所有进程执行完毕 print('-'*10+'多进程执行结束,结果如下:'+'-'*10) for res in results: print(res.get()) def main(): numbers = [10000000 + x for x in range(20)] #串行执行方式 start_time = time.perf_counter() calculate_sums(numbers) end_time = time.perf_counter() print('串行执行用时:Calculation takes {} seconds'.format(end_time - start_time)) #多进程执行方式 start_time = time.perf_counter() multicalculate_sums(numbers) end_time = time.perf_counter() print('多进程执行用时:Calculation takes {} seconds'.format(end_time - start_time)) if __name__ == '__main__': main()
作者回复: 很棒的例子,但是对计算密集型程序,你可以打开任务管理器的性能页,CPU 选择显示逻辑处理器,可以注意到串行执行和并行执行的不同。
2019-10-2126 - szc能否举一些例子,哪些场景是IO密集型中的IOheavy, 那些是IO很快
作者回复: 这个得看具体场景。比如大公司里相应业务爬虫的规模非常大,要抓取百万级的视频新闻信息流,这种就属于IO heavy。但是如果你只需要抓取10个网站的信息,并且网络连接良好,那么IO就很快
2019-06-291 - 阿卡牛常听到阻塞,同步是不是就是阻塞地意思
作者回复: 通俗来讲是
2019-11-012 - helloworld总结多线程和协程之间的共同点和区别: 共同点: 都是并发操作,多线程同一时间点只能有一个线程在执行,协程同一时间点只能有一个任务在执行; 不同点: 多线程,是在I/O阻塞时通过切换线程来达到并发的效果,在什么情况下做线程切换是由操作系统来决定的,开发者不用操心,但会造成race condition; 协程,只有一个线程,在I/O阻塞时通过在线程内切换任务来达到并发的效果,在什么情况下做任务切换是开发者决定的,不会有race condition的情况; 多线程的线程切换比协程的任务切换开销更大; 对于开发者而言,多线程并发的代码比协程并发的更容易书写。 一般情况下协程并发的处理效率比多线程并发更高。2019-06-28359
- hlz-1231、单进程,老师的原程序,运行时间 Calculation takes 15.305913339 seconds 2、CPU并行方式,运行时间: Calculation takes 3.457259904 seconds def calculate_sums(numbers): with concurrent.futures.ProcessPoolExecutor() as executor: executor.map(cpu_bound,numbers) 3、多线程,cocurrent.futures,运行时间 Calculation takes 15.331446270999999 seconds def calculate_sums(numbers): with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor: executor.map(cpu_bound,numbers) 4、异步方式,asyncio Calculation takes 16.019983702999998 seconds async def cpu_bound(number): print(sum(i * i for i in range(number))) async def calculate_sums(numbers): tasks=[asyncio.create_task(cpu_bound(number)) for number in numbers] await asyncio.gather(*tasks)2019-06-28430
- 天凉好个秋如果完成,则将其放到预备状态的列表; 如果未完成,则继续放在等待状态的列表。 这里是不是写的有问题? PS:想问一下,完成之后为什么还要放队列里?难道不应该从队列里移除吗?2019-06-2828
- transformationimport time from concurrent import futures def cpu_bound(number): return sum(i * i for i in range(number)) def calculate_sums(numbers): for number in numbers: print(cpu_bound(number)) def main(): start_time = time.perf_counter() numbers = [10000000 + x for x in range(20)] calculate_sums(numbers) end_time = time.perf_counter() print('Calculation takes {} seconds'.format(end_time - start_time)) def main_process(): start_time = time.perf_counter() numbers = [10000000 + x for x in range(20)] with futures.ProcessPoolExecutor() as pe: result = pe.map(cpu_bound, numbers) print(f"result: {list(result)}") end_time = time.perf_counter() print('multiprocessing Calculation takes {} seconds'.format(end_time - start_time)) if __name__ == '__main__': main() main_process() ———————— 输出: 333333283333335000000 333333383333335000000 333333483333355000001 333333583333395000005 333333683333455000014 333333783333535000030 333333883333635000055 333333983333755000091 333334083333895000140 333334183334055000204 333334283334235000285 333334383334435000385 333334483334655000506 333334583334895000650 333334683335155000819 333334783335435001015 333334883335735001240 333334983336055001496 333335083336395001785 333335183336755002109 Calculation takes 15.771127400000001 seconds result: [333333283333335000000, 333333383333335000000, 333333483333355000001, 333333583333395000005, 333333683333455000014, 333333783333535000030, 333333883333635000055, 333333983333755000091, 333334083333895000140, 333334183334055000204, 333334283334235000285, 333334383334435000385, 333334483334655000506, 333334583334895000650, 333334683335155000819, 333334783335435001015, 333334883335735001240, 333334983336055001496, 333335083336395001785, 333335183336755002109] multiprocessing Calculation takes 4.7333084 seconds2019-06-2858
- Paul Shansync是线性前后执行。 async是穿插执行,之所以要穿插,代码需要的资源不同,有的代码需要CPU,有的代码需要IO(例如网络),穿插以后,同时需要CPU和网络的代码可以同时执行,充分利用硬件。 具体到关键字 async 是表示函数是异步的,也就是来回穿插的起点(进入预备队列),await是表示调用需要IO,也就是进入等待队列的入口(函数开始调用)和出口(函数调用结束,重新进入预备队列)。2019-11-213
- 唐哥老师好,对于 Asyncio 来说,它的任务在运行时不会被外部的一些因素打断。不被打断是如何保证的?还有event loop是每次取出一个任务运行,当这个任务运行期间它就是只等待任务结束吗?不干其他事了吗?2019-07-0123