自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

一直讓 PHP 程序員懵逼的同步阻塞異步非阻塞,終于搞明白了

開發(fā) 前端
我們先來看同步阻塞的例子,使用 socket_create、socket_bind、socket_listen 函數(shù)創(chuàng)建綁定并監(jiān)聽了 8080 端口,然后一直阻塞在 socket_accept 函數(shù)上,直到有客戶端連接的到來。

大家好,我是碼農(nóng)先森。

經(jīng)常聽到身邊寫 Java、Go 的朋友提到程序異步、非阻塞、線程、協(xié)程,讓系統(tǒng)性能提高到百萬、千萬并發(fā),使我甚是驚訝屬實羨慕。對于常年寫 PHP 的我來說,最初聽到這幾個詞時,腦袋一直處于蒙圈狀態(tài),回過頭來看著自己手上同步阻塞的 PHP 代碼,心想著「異步、非阻塞、線程、協(xié)程」到底是個什么東東,這么厲害嘛。其實 PHP 中也有線程、協(xié)程,但在日常的編程中幾乎不會使用,原因是 PHP-FPM 多進程模式下并不支持線程、協(xié)程,使用 PHP 編程的程序員絕大多數(shù)都離不開 PHP-FPM 。這也就導致了 PHP 程序員對那些概念沒有接觸,那就更別提理解了,因此為了廣大的 PHP 程序員同胞們能夠和 Java、Go 的程序員對上話,特地對「同步、阻塞、異步、非阻塞」這幾個概念進行了深度的分析,爭取把 PHP 程序員的腰桿挺直溜。

按照慣例先上八股文這道菜:

  • 同步阻塞:當一個操作被調(diào)用時,調(diào)用者將被阻塞,直到這個操作完成并返回結果。在此期間,調(diào)用者無法進行其他任務。
  • 異步阻塞:當一個操作被調(diào)用時,調(diào)用者不會被阻塞,而是可以繼續(xù)執(zhí)行其他任務。然而,它仍然需要等待被調(diào)用的操作完成,并在操作完成后處理其結果。這個等待過程可能是阻塞的。
  • 同步非阻塞:調(diào)用者發(fā)起一個操作后,不會被阻塞并可以繼續(xù)執(zhí)行其他任務。雖然調(diào)用者可以立即獲得控制權,但它仍然需要等待操作完成才能處理結果。在等待的過程中,調(diào)用者可以主動輪詢或者不斷嘗試獲取操作結果,以避免長時間的阻塞。
  • 異步非阻塞:調(diào)用者發(fā)起一個操作后,不會被阻塞并可以繼續(xù)執(zhí)行其他任務。同時,調(diào)用者也不需要等待操作完成來處理結果。相反,調(diào)用者可以注冊一個回調(diào)函數(shù)或者使用類似事件驅(qū)動的機制,當操作完成后被自動觸發(fā)回調(diào)函數(shù)來處理結果。

基礎知識扎實的朋友看這個八股文就足以解惑了,不過看得懂八股文的畢竟是少數(shù)英俊帥氣人,你說氣不氣人集顏值與才華于一體,別看說的就是各位看官「哈哈」。言歸正傳,那看不懂八股文的怎么搞?別急,且聽我結合生活中的例子娓娓道來。

你每天上班匆匆路過的早餐店,今天額外的多人,你湊近一看原來是來了位身材高挑楚楚動人的美女服務員,結果你按耐不住心中的激動,今天高低得買兩個饅頭外加一杯豆?jié){,由于買的人太多,蒸好的饅頭早已賣完,這時你只能等正在蒸的,期間你什么也干不了只能眼勾勾的干等著,那么這時的你是同步阻塞的。

由于來買早餐的人越來越多,離上班的時間也越來越近,你開始了騷動,每隔幾分鐘就問美女服務員饅頭蒸好了沒?此時的你不再干等,而是開始刷刷抖音看看工作群,因為你已經(jīng)付錢了所以還是得等饅頭,由于美女服務員太忙了沒空主動告訴你,需要你自己不斷地問,那么這時的你是同步非阻塞的。

過了高峰期人變少了,視野更廣闊了,你看美女服務員更清楚了,結果你又開始眼勾勾的干等著,抖音也不刷了工作群的消息也不顧了。由于美女服務員不忙了,開始主動叫那位身穿格子衫背雙肩包帥哥,饅頭蒸好了,這時的你甩了甩頭上的劉海,接過了美女服務員手中的饅頭會心一笑,順便還加了對方的微信,那么此時的你是異步阻塞的。

隔天你為了再睹芳容,又來到了這家早餐店,一向摳門的你甩手就點了兩個肉包。這時美女服務員迎面笑臉告知你肉包還需耐心等待哦,蒸好了會微信通知你。在炎炎的夏日里你路上走的太匆忙,此時的你口渴難耐,就去隔壁小賣部買了瓶82年的可樂,還坐著吹了會空調(diào)。隨著微信的一聲叮咚,你起身去早餐店,接過了美女服務員手中的肉包,那么此時的你是異步非阻塞的。

有了美女服務員的投喂,你工作的干勁都十足了,同時應該也把「同步、阻塞、異步、非阻塞」這幾個概念搞懂了吧。其實這里的同步異步和阻塞非阻塞,容易搞混淆就像你看美女服務員容易丟魂一樣,在這個例子中同步異步需要關注的是「美女服務員是否會主動的通知你」,主動通知你那么對你來說就是異步的,需要你去詢問那么對你來說就是同步的。阻塞非阻塞需要關注的點是「你是否是眼勾勾的干等著」,如果你只能干等那就是阻塞的,如果你還能干點其他的事情比如刷抖音、買82年的可樂,那么就是非阻塞的。

美女也看了道理也懂了,有的朋友們又要產(chǎn)生新的疑問了,那在程序中怎么體現(xiàn)、怎么用「同步、阻塞、異步、非阻塞」呢?那我們就開始上代碼,畢竟看美女服務員的目的也是為了能夠深入交往嘛,也就等同于實踐上手了,你細品是不是這個理。

開整!

我們先來看同步阻塞的例子,使用 socket_create、socket_bind、socket_listen 函數(shù)創(chuàng)建綁定并監(jiān)聽了 8080 端口,然后一直阻塞在 socket_accept 函數(shù)上,直到有客戶端連接的到來。傳統(tǒng)的 PHP-FPM 就是同步阻塞的模式,不過 PHP-FPM 多進程模型,在接收到客戶端連接 $client 后就交給由子進程進行后續(xù)的處理了,在這個例子只舉例了單進程的模式。

<?php

// 同步阻塞模式

// 創(chuàng)建一個監(jiān)聽 Socket
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

// 綁定 8080 端口
socket_bind($socket, '0.0.0.0', 8080);

// 開始監(jiān)聽
socket_listen($socket);

while(true){
    // 會阻塞在這里,一直等著客戶端來連接
    // 結合剛剛的例子可以理解為,你一直在這里眼勾勾的干等饅頭,啥也干不了
    $client = socket_accept($socket);
    if($client){
        echo "客官來了" . PHP_EOL;
    }
}

再來看看同步非阻塞的例子,同樣也是監(jiān)聽了 8080 端口,不同的是將套接字 $socket 設置成了非阻塞模式。那么這種情況下將不會一直阻塞在 socket_accept 函數(shù)上,會繼續(xù)往下執(zhí)行,如果沒有寫其他的邏輯,就會出現(xiàn)放空炮的現(xiàn)象。這種模式在實際的編程中基本上不會采用,會把系統(tǒng)榨干,這一點值得注意一下,誰寫了這樣的代碼就要拉出去罰站了。

<?php

// 同步非阻塞模式

// 創(chuàng)建一個監(jiān)聽 Socket
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

// 綁定 8080 端口
socket_bind($socket, '0.0.0.0', 8080);

// 開始監(jiān)聽
socket_listen($socket);

// 這里設置成非阻塞
socket_set_nonblock($socket);

while(true){
    // 不會阻塞在這里
    $client = socket_accept($socket);
    if($client){
        echo "客官來了" . PHP_EOL;
    }

    // 會繼續(xù)往下執(zhí)行
    // 結合剛剛的例子,可以在這里刷刷抖音、看看工作群消息等
    // ...

    // 如果你上面沒有寫任何的邏輯,這些最好 sleep 一下
    // 不然 CPU 就會被榨干了,也就是說不要一直眼勾勾的盯著美女服務員會被吸干
    // 要適當?shù)男菹⒁幌?    sleep(5);
}

繼續(xù)接著看看異步阻塞的例子,還好有 Swoole 不然這種模式的例子都沒有地方找了,這里感謝一下 Swoole 為 PHP 程序員做的貢獻,讓我們硬氣了一回。構造一個 HTTP 服務并監(jiān)聽了 9501 端口,然后設置了針對 Request 的異步回調(diào)函數(shù),但如果在回調(diào)函數(shù)里面使用了類似 sleep、PDO 等的 PHP 原生函數(shù),就會阻塞整個進程,導致無法處理其他的 Requset 請求。這種情況下的程序性能直接和同步阻塞等同了,所以異步阻塞模式在實際的編程實踐中也不常用,還不如使用同步阻塞模式了。這里提醒一點,在新版的 Swoole 中已經(jīng)可以通過 HOOK 的方式支持 PHP 原生函數(shù)協(xié)程化了,這一點也值得慶幸。

<?php

// 異步阻塞模式

// 創(chuàng)建一個 Swoole 的異步 HTTP 服務器
$http = new Swoole\Http\Server('127.0.0.1', 9501);

// 設置異步回調(diào)函數(shù)
$http->on('request', function ($request, $response) {
    // 阻塞了整個進程,使用 PHP 原生的 PDO、Redis 等都會阻塞當前進程
    // 結合剛剛的例子,只能干等著,這里你啥也干不了
    sleep(5);

    $response->end("OK");
});

// 啟動服務器
$http->start();

最后來看看異步非阻塞的例子,這種模式是目前在實踐中性能最好的,和上面例子唯一不同的是在 Request 回調(diào)函數(shù)中使用了協(xié)程類,便不會阻塞整個進程,能夠釋放出 CPU 的控制權去處理其他的請求。當然在新版的 Swoole 中也不一定需要使用協(xié)程類,使用原生的函數(shù)同樣不會阻塞進程了,這一點大大減低了 PHP 程序員編程的心智負擔。

<?php

// 異步非阻塞模式

// 創(chuàng)建一個 Swoole 的異步 HTTP 服務器
$http = new Swoole\Http\Server('127.0.0.1', 9501);

// 設置異步回調(diào)函數(shù)
$http->on('request', function ($request, $response) {
    // 不會阻塞整個進程,這里還可以使用類似其他的協(xié)程客戶端
    // swoole\Coroutine\MySQL
    // swoole\Coroutine\Redis
    // 結合剛剛的例子,這里你可以去刷抖音、買82年的可樂
    // 也是說你有空去處理其他的請求了,不用這里干等
    // 等5秒過后,又可以回來繼續(xù)向下執(zhí)行,接過肉包之后你就可以上班去了,雖然你有百般不舍。
    Co::sleep(5);

    $response->end("OK");
});

// 啟動服務器
$http->start();

雖然你依然忘不了早餐店美女服務員的容顏,但空空的口袋催促著你趕緊去上班了。看到這里你既欣賞了美女的容顏,同時又把「同步、阻塞、異步、非阻塞」也搞懂了,簡直兩全其美,了解了這些概念對以后學習 Go 語言也大有裨益。但是大家都知道這么一個道理,看懂了并不等于真的懂了,很多人一看就會一做就廢,因此最好自己上手實踐一下,在知中行,在行中知,做到知行合一,就像看美女服務員不是目的而是想要更深入一步交流,就此打住哈哈。在市面上絕大多數(shù)的高性能程序都是異步非阻塞的模式,比如 Nginx、Redis 等,如果大家想寫出高性能的程序最好是優(yōu)先考慮這種模式,因為借鑒才是最快的學習方法。本次分享的內(nèi)容到就此結束了,希望對大家能有所幫助。

責任編輯:武曉燕 來源: 碼農(nóng)先森
相關推薦

2019-07-23 11:01:57

Python同步異步

2012-02-22 21:15:41

unixIO阻塞

2012-10-10 10:00:27

同步異步開發(fā)Java

2024-09-23 17:15:28

Python并發(fā)并行

2018-03-28 08:52:53

阻塞非阻塞I

2015-07-03 10:12:04

編程同步非阻塞

2021-03-04 08:34:55

同步阻塞非阻塞

2021-06-04 18:14:15

阻塞非阻塞tcp

2020-06-28 11:44:02

IO模型計算機

2021-02-27 16:08:17

Java異步非阻塞

2024-09-05 09:41:57

2022-06-22 08:16:29

異步非阻塞框架

2023-12-06 07:28:47

阻塞IO異步IO

2019-05-05 08:50:42

阻塞非阻塞BIO

2022-11-10 10:29:07

KPI軟件開發(fā)

2020-02-08 19:13:29

程序員搞明白CDN

2025-02-17 13:23:34

Python同步阻塞MySQL

2020-01-19 10:54:17

CDN邊緣計算域名

2016-11-04 12:51:46

Unix網(wǎng)絡IO 模型

2021-10-11 10:41:14

TCP傳輸層協(xié)議網(wǎng)絡
點贊
收藏

51CTO技術棧公眾號