編程學(xué)習(xí)網(wǎng) > 編程語(yǔ)言 > Python > 說(shuō)說(shuō)下面幾個(gè)Python概念:同步,異步,阻塞,非阻塞?
2025
09-18

說(shuō)說(shuō)下面幾個(gè)Python概念:同步,異步,阻塞,非阻塞?


說(shuō)到“同步”、“異步”、“阻塞”、“非阻塞”這四個(gè)詞,很多程序員剛?cè)胄械臅r(shí)候都容易混淆,尤其是在面試的時(shí)候,被問(wèn)到這幾個(gè)詞是啥意思,腦袋一懵,全靠嘴硬撐過(guò)去。我當(dāng)年面試的時(shí)候也被這幾個(gè)詞搞得暈頭轉(zhuǎn)向,但真正在項(xiàng)目里踩過(guò)幾次坑之后,才慢慢搞明白這四個(gè)概念到底是怎么回事。今天就拿Python舉例子,結(jié)合我自己的經(jīng)驗(yàn),跟大家聊聊這幾個(gè)老生常談但又經(jīng)常讓人抓狂的概念。

先說(shuō)“同步”和“異步”。這倆最容易被搞混,聽(tīng)起來(lái)像是操作系統(tǒng)級(jí)別的高大上詞匯,其實(shí)說(shuō)白了就是一個(gè)事做完了再做下一個(gè),和可以同時(shí)干幾件事的區(qū)別。

在Python里,同步代碼就是最常見(jiàn)的那種寫(xiě)法,比如:


這段代碼執(zhí)行下來(lái)要3秒,抓數(shù)據(jù)的時(shí)候程序就卡那兒等,等數(shù)據(jù)抓完了再去處理。這就叫同步,典型的一步一步走流程,像是排隊(duì)打飯,前面的人不動(dòng),你就只能干等著。

那異步是啥?我們看看Python里用 async 和 await 寫(xiě)的異步版本:


這個(gè)程序總共只用2秒就搞定了,因?yàn)樽?shù)據(jù)和處理數(shù)據(jù)是同時(shí)開(kāi)始的,誰(shuí)先完事兒就先輸出。就像打飯窗口換成了食堂阿姨們齊上陣,多個(gè)窗口同時(shí)開(kāi)工,效率蹭蹭的上去了。這就是異步,不再傻等,能干的先干,干不了的放一邊。

這時(shí)候你可能會(huì)問(wèn)了,那“阻塞”和“非阻塞”又是啥意思?聽(tīng)起來(lái)和同步異步很像,其實(shí)還是有點(diǎn)差別的。它們主要是描述調(diào)用一個(gè)操作后,程序是否會(huì)被“卡住”。

“阻塞”就是說(shuō)你執(zhí)行了某個(gè)操作,比如說(shuō)讀文件、訪問(wèn)網(wǎng)絡(luò),這玩意兒要是沒(méi)給你結(jié)果,你就只能等在那兒,一動(dòng)不動(dòng),哪怕你CPU很空閑,內(nèi)存也足夠,它也不會(huì)去做別的事。

比如這個(gè)經(jīng)典的阻塞IO:


程序會(huì)在 recv 那里一直等服務(wù)器回?cái)?shù)據(jù),等不到就不動(dòng)。這種就是阻塞IO,誰(shuí)用誰(shuí)知道,真的煩。

非阻塞就不一樣了,它不會(huì)等你,直接返回,然后你可以自己輪詢或者注冊(cè)回調(diào),再回來(lái)處理數(shù)據(jù)。用Python寫(xiě)個(gè)簡(jiǎn)單的非阻塞版本是這樣的:

這個(gè)看起來(lái)就復(fù)雜多了,畢竟你得時(shí)刻盯著 socket 有沒(méi)有數(shù)據(jù)回來(lái),但好處是程序不會(huì)被卡住,可以繼續(xù)做別的事情。

說(shuō)到這里其實(shí)可以畫(huà)個(gè)象限圖來(lái)理解這四個(gè)概念:

  • 同步阻塞:最傳統(tǒng)的調(diào)用方式,等一個(gè)事做完才繼續(xù),比如requests庫(kù)
  • 異步非阻塞:最高效的組合,適合高并發(fā),比如 asyncio + aiohttp
  • 同步非阻塞:雖然同步但不會(huì)卡住,比如用 settimeout 控制時(shí)間
  • 異步阻塞:這種比較罕見(jiàn),但也不是不可能,比如你用 async 寫(xiě)了協(xié)程但里面用了 time.sleep 這種阻塞操作,那你還是得傻等

別看概念聽(tīng)起來(lái)很學(xué)術(shù),其實(shí)我們?cè)趯?shí)際開(kāi)發(fā)中經(jīng)常遇到這些場(chǎng)景。比如你寫(xiě)個(gè)爬蟲(chóng),幾十上百個(gè)URL要抓,你要是同步阻塞一個(gè)個(gè)抓,那就等到花兒都謝了。但你換成 aiohttp 來(lái)搞異步請(qǐng)求,十幾個(gè)協(xié)程一上,效率立馬提升好幾倍。

再比如處理用戶請(qǐng)求的時(shí)候,如果你寫(xiě)了一個(gè)接口,這個(gè)接口要去調(diào)外部API,還得訪問(wèn)數(shù)據(jù)庫(kù),如果你用阻塞的方式寫(xiě),那用戶請(qǐng)求來(lái)了只能等你慢悠悠跑完。但如果用異步非阻塞的方式,你就可以同時(shí)處理很多請(qǐng)求,提高吞吐量。

不過(guò)話說(shuō)回來(lái),異步雖然香,但也不是銀彈。我以前在一個(gè)項(xiàng)目里,為了提高并發(fā),把整個(gè)接口都用 asyncio 重寫(xiě)了一遍,結(jié)果整個(gè)團(tuán)隊(duì)搞得焦頭爛額,debug 成了噩夢(mèng)。一個(gè) await 寫(xiě)錯(cuò)地方,程序就會(huì)莫名其妙卡住,查日志查了兩天才找到問(wèn)題。所以我覺(jué)得異步適合用在那種IO密集型的場(chǎng)景,比如網(wǎng)絡(luò)請(qǐng)求、磁盤讀寫(xiě)、數(shù)據(jù)庫(kù)操作,但如果是CPU密集型的工作,比如視頻轉(zhuǎn)碼、圖像處理,那還是乖乖用多線程或多進(jìn)程靠譜。

還有個(gè)容易被忽略的點(diǎn)就是,Python 的 GIL(全局解釋器鎖)也會(huì)影響到線程的表現(xiàn)。你以為你開(kāi)了十幾個(gè)線程就能同時(shí)執(zhí)行,結(jié)果發(fā)現(xiàn)一個(gè)CPU核心在那兒獨(dú)舞,其他線程都在邊上看熱鬧。所以現(xiàn)在大家越來(lái)越傾向用 asyncio 來(lái)搞高并發(fā),既能繞過(guò) GIL 的限制,又能寫(xiě)出結(jié)構(gòu)清晰的代碼。

最后再講個(gè)小故事。有一次我們給客戶做一個(gè)高并發(fā)的API網(wǎng)關(guān),那客戶的業(yè)務(wù)峰值能到每秒上千請(qǐng)求,用傳統(tǒng)的阻塞模型壓根撐不住。我就拍著胸脯說(shuō)這事兒我來(lái),直接用Python的asyncio擼了個(gè)異步網(wǎng)關(guān),接入層用 aiohttp,后端調(diào)用數(shù)據(jù)庫(kù)用aiomysql,再配合redis緩存,輕輕松松抗住了高峰。后來(lái)客戶說(shuō)“你這系統(tǒng)怎么還沒(méi)宕機(jī)?”,我說(shuō)“你這業(yè)務(wù)量太小了,連我日志都沒(méi)打滿一頁(yè)”。

當(dāng)然,寫(xiě)異步代碼是門手藝活,一不小心就容易寫(xiě)成意大利面,維護(hù)成本很高。所以我個(gè)人的建議是,如果你是一個(gè)小項(xiàng)目、小團(tuán)隊(duì),不要一開(kāi)始就上異步,先把同步寫(xiě)穩(wěn)了,性能實(shí)在不行再考慮異步。如果你是寫(xiě)底層框架或者要支撐百萬(wàn)并發(fā)的系統(tǒng),那異步就是你的老朋友。

總之,同步異步,阻塞非阻塞這些概念,得看場(chǎng)景來(lái)選,不要盲目追新。就像我?guī)煾诞?dāng)年跟我說(shuō)的那句話:“技術(shù)不是炫技,是為了解決問(wèn)題。”各位兄弟們共勉吧。

以上就是“Python大模型應(yīng)用開(kāi)發(fā)的核心技術(shù)有哪些?的詳細(xì)內(nèi)容,想要了解更多Python教程歡迎持續(xù)關(guān)注編程學(xué)習(xí)網(wǎng)。

掃碼二維碼 獲取免費(fèi)視頻學(xué)習(xí)資料

Python編程學(xué)習(xí)

查 看2022高級(jí)編程視頻教程免費(fèi)獲取