国产日韩欧美一区二区三区三州_亚洲少妇熟女av_久久久久亚洲av国产精品_波多野结衣网站一区二区_亚洲欧美色片在线91_国产亚洲精品精品国产优播av_日本一区二区三区波多野结衣 _久久国产av不卡

?

Python函數(shù)并行執(zhí)行方法的研究與實(shí)踐

2021-03-15 07:01:33秦子實(shí)
電腦知識(shí)與技術(shù) 2021年3期

秦子實(shí)

摘要:編程語言Python因其簡潔的語法、強(qiáng)大的表達(dá)能力、方便的基礎(chǔ)庫以及豐富的第三方庫,被廣泛應(yīng)用于各個(gè)行業(yè)的日常業(yè)務(wù)中。近年來,隨著Python大版本的更新,引入了越來越多的并行執(zhí)行基礎(chǔ)庫,以方便編程人員優(yōu)化各種場景下的代碼執(zhí)行效率。本文介紹了Python在近幾個(gè)版本中新引入的并行庫在常見場景下的代碼執(zhí)行效率優(yōu)化方法,方法具有代碼改動(dòng)小、優(yōu)化效果顯著、方便部署等特點(diǎn),適合Python使用者在各場景中的程序執(zhí)行效率優(yōu)化。

關(guān)鍵詞:多進(jìn)程;異步函數(shù);Python

中圖分類號(hào):TP393? ? ? 文獻(xiàn)標(biāo)識(shí)碼:A

文章編號(hào):1009-3044(2021)03-0050-02

1 概述

隨著Python語言在各行各業(yè)的廣泛使用,代碼規(guī)模和執(zhí)行任務(wù)的數(shù)量都顯著增長,因此,部分代碼的執(zhí)行效率優(yōu)化越來越重要。同其他流行編程語言類似,新版的Python同樣引入了越來越多并行執(zhí)行基礎(chǔ)庫,例如3.3版本以來大量引入的多進(jìn)程庫multiprocessing,以及3.6版本以來大量引入的協(xié)程庫asyncio等。Python的諸多應(yīng)用場景,均存在使用多核CPU執(zhí)行多任務(wù)程序的情形,例如通過網(wǎng)絡(luò)下載的線程任務(wù),或是利用win32的API操作COM的進(jìn)程任務(wù)。若使用單進(jìn)程同步阻塞運(yùn)行,則不能充分利用硬件的算力和I/O帶寬,大量的CPU周期浪費(fèi)在等待響應(yīng)中,導(dǎo)致執(zhí)行時(shí)間成倍增長。

本文針對(duì)上述需求,根據(jù)Python并發(fā)庫的特點(diǎn),按照不同場景研究并實(shí)踐了一套任務(wù)并行開發(fā)方法。該方法利用Python基礎(chǔ)庫,可以在現(xiàn)有代碼上實(shí)現(xiàn)大幅提升阻塞式代碼的執(zhí)行效率。

2 進(jìn)程并行

2.1 應(yīng)用場景

在特定場景下,代碼必須在進(jìn)程環(huán)境下運(yùn)行,比較常見的是在使用pywin32的API操作COM組件的場景下,例如使用Python操作Office或建立WMI連接,此類場景中,必須為COM組件建立單獨(dú)的進(jìn)程。本文以WMI連接為例,介紹多進(jìn)程并發(fā)建立WMI連接。

2.2 實(shí)現(xiàn)方案

并發(fā)多個(gè)WMI連接時(shí),每個(gè)連接均需要使用單獨(dú)的進(jìn)程發(fā)起。對(duì)于此類并發(fā)多個(gè)相同或相似進(jìn)程的場景,通常使用multiprocessing.pool.Pool類創(chuàng)建進(jìn)程池實(shí)例,進(jìn)而使用apply、map系列函數(shù)。

需要注意的是,在Windows系統(tǒng)上,當(dāng)使用multiprocessing的程序有凍結(jié)并生成Windows可執(zhí)行程序的需求時(shí)(例如使用py2exe、PyInstaller、cx_Freeze等打包程序),需要在使用多進(jìn)程模塊前調(diào)用freeze_support函數(shù)。

在使用進(jìn)程池建立WMI連接時(shí),使用Pool創(chuàng)建進(jìn)程池,并使用參數(shù)processes指定進(jìn)程數(shù),通常進(jìn)程數(shù)小于邏輯核心數(shù)。

之后使用pool.imap_unordered生成一個(gè)進(jìn)程池的可迭代對(duì)象,將WMI連接函數(shù)read_wmi_func傳入,并指定一個(gè)IP列表ip_list作為進(jìn)程池中所有進(jìn)程的參數(shù)。該可迭代對(duì)象用于遍歷并得到進(jìn)程池中每個(gè)進(jìn)程的返回值。

該函數(shù)的行為可概括為:創(chuàng)建若干個(gè)worker進(jìn)程(數(shù)目由processes參數(shù)指定),每個(gè)worker初始化環(huán)境并運(yùn)行read_wmi_func函數(shù),并從ip_list中依次取出每個(gè)成員作為參數(shù)傳給各worker中的函數(shù)。

需要了解的是,“imap_unordered”為apply、map系列函數(shù)中的一個(gè),函數(shù)名中的“i”即iterable,說明該函數(shù)返回可迭代對(duì)象,是一個(gè)惰性求值函數(shù);“map”指分發(fā)任務(wù)的方式為map映射;“unordered”說明該函數(shù)在使用參數(shù)集合ip_list創(chuàng)建新進(jìn)程時(shí),不會(huì)依次等待前一個(gè)進(jìn)程結(jié)束,即該函數(shù)的worker在完成當(dāng)前任務(wù)后立刻取下一個(gè)參數(shù)并執(zhí)行,而不是等待前面的worker執(zhí)行完成再依次取參數(shù)。apply、map系列函數(shù)的命名均遵循此類規(guī)則。在示例的WMI場景中,任務(wù)直接并無先后次序,也無須相互等待依次完成,因此,使用“imap_unordered”的效率較高。

3 協(xié)程異步

3.1 技術(shù)背景

Python的協(xié)程解決方案是在3.6版本后逐步引入的,總體而言,協(xié)程是基于同一個(gè)線程的,即協(xié)程是單線程的。與以往的多線程解決方案不同的是,多線程通常是多個(gè)線程運(yùn)行多個(gè)任務(wù),每個(gè)任務(wù)都是阻塞式,即該線程中的任務(wù)執(zhí)行完成后,才能繼續(xù)執(zhí)行下一個(gè)進(jìn)程。協(xié)程在一個(gè)線程中維護(hù)一個(gè)事件循環(huán),協(xié)程中執(zhí)行的事件均為非阻塞的,即一個(gè)事件執(zhí)行后立即接著執(zhí)行下一個(gè)事件,事件循環(huán)會(huì)輪詢檢查各事件是否執(zhí)行完成,完成的獲得返回值或執(zhí)行回調(diào)函數(shù)。

多線程方案通常存在資源鎖及搶占問題,在多個(gè)線程同時(shí)訪問資源產(chǎn)生;而協(xié)程為同一個(gè)線程,在不同的時(shí)間進(jìn)行訪問,只需要梳理清楚執(zhí)行流程,即可以同步方式編寫異步程序。

3.2 應(yīng)用場景

以非阻塞方式運(yùn)行一個(gè)Python函數(shù),一般有兩個(gè)應(yīng)用場景:將一個(gè)普通Python函數(shù)放入事件循環(huán)執(zhí)行,或是直接執(zhí)行一個(gè)異步函數(shù)。

目前,仍有大量Python庫尚不包含異步API,使用asyncio對(duì)普通函數(shù)進(jìn)行封裝,并異步執(zhí)行普通Python函數(shù)有較多的應(yīng)用場景。而有部分常見庫的最新版本已經(jīng)支持了異步API,支持直接通過asyncio.run()異步執(zhí)行。

3.3 異步執(zhí)行普通函數(shù)

使用asyncio執(zhí)行普通函數(shù),需要先將普通函數(shù)封裝為future對(duì)象,然后將這些future對(duì)象傳給asyncio.gather統(tǒng)一執(zhí)行,并返回結(jié)果。http下載就是較為常見的場景,例如需要獲取某鏈接列表中的所有url內(nèi)容,假設(shè)列表中有5個(gè)鏈接:同步模式下,即在循環(huán)中使用requests.get依次下載列表中的所有url內(nèi)容并將其結(jié)果放入結(jié)果列表中,如此,需要的總時(shí)間大約為內(nèi)容傳輸以及網(wǎng)絡(luò)延遲時(shí)間的5倍;異步模式下,asyncio將一次性發(fā)送5個(gè)GET請(qǐng)求并等待遠(yuǎn)端返回,在遠(yuǎn)端全部返回結(jié)果后結(jié)束并一次性返回所有結(jié)果,需要的總時(shí)間大約為所有GET請(qǐng)求中最慢的一個(gè)請(qǐng)求時(shí)間。

上述示例的返回即為有5個(gè)“”對(duì)象的列表,經(jīng)測(cè)試,總時(shí)長約等于url請(qǐng)求的平均時(shí)常。示例中的代碼將requests.get這一普通函數(shù)通過事件循環(huán)的run_in_executor封裝為future對(duì)象,再使用gather統(tǒng)一執(zhí)行這些future對(duì)象。

3.4 異步執(zhí)行協(xié)程

同樣使用http下載的例子,httpx是一個(gè)與requests庫類似且具有異步API的常用網(wǎng)絡(luò)工具庫。

需要注意的是,在循環(huán)中創(chuàng)建協(xié)程,應(yīng)當(dāng)使用一個(gè)循環(huán)創(chuàng)建,再使用另一個(gè)循環(huán)等待結(jié)果,而不是在同一個(gè)循環(huán)中創(chuàng)建一個(gè)協(xié)程就等待一次結(jié)果,如此編寫則為使用同步方式運(yùn)行協(xié)程,失去了異步方式提升I/O效率的功能。

4 結(jié)束語

本文介紹了在諸如I/O等存在較長阻塞時(shí)間的場景中,提升代碼執(zhí)行效率的兩種常見方式,在Python中恰當(dāng)?shù)厥褂枚噙M(jìn)程或協(xié)程可以成倍提升代碼執(zhí)行效率。特別的,Python默認(rèn)C語言實(shí)現(xiàn)存在線程的全局解釋器鎖(global interpreter lock),因此正確的利用協(xié)程解決方案可以極大地提升Python線程的執(zhí)行效率。

【通聯(lián)編輯:梁書】

东阳市| 伽师县| 普陀区| 阿城市| 离岛区| 洪湖市| 刚察县| 鄂州市| 云林县| 上栗县| 遵义县| 大宁县| 横峰县| 马尔康县| 花莲市| 宝清县| 长春市| 大田县| 盱眙县| 顺昌县| 岫岩| 宣汉县| 隆安县| 澄江县| 定安县| 雷山县| 永丰县| 铁岭市| 安西县| 新蔡县| 青田县| 华池县| 乌兰察布市| 荔浦县| 九台市| 揭阳市| 垣曲县| 抚宁县| 临城县| 龙胜| 洱源县|