動態

詳情 返回 返回

程序的同步與異步 - 動態 詳情

前言:

在計算機網絡通訊中有兩種傳輸方式。同步傳輸和異步傳輸。同步傳輸好比一座單行的高架橋,
異步傳輸好比一座雙通道的高架橋。同樣在程序代碼中也存在同步和異步的執行方式。先來了解一下程序的中幾個概念

進程和線程

線程與進程相比更輕量,而且線程之間是共享內存堆棧的,所以不同的線程之間交互非常容易實現。比如聊天室這樣的程序,客户端連接之間可以交互,比聊天室中的玩家可以任意的其他人發消息。用多線程模式實現非常簡單,線程中可以直接向某一個客户端連接發送數據。而多進程模式就要用到管道、消息隊列、共享內存,統稱進程間通信(IPC)複雜的技術才能實現。

線程是調度的基本單位,而進程則是資源擁有的基本單位。

在計算機編程中,進程(Process)和線程(Thread)是兩個重要的概念,用於管理和執行程序的併發性和並行性。

進程是指計算機中正在運行的程序的實例。每個進程都有自己的地址空間、內存、文件描述符、環境變量等。進程之間是相互獨立的,它們通過操作系統進行管理和調度。每個進程都會被分配一個唯一的進程標識符(PID)。

進程可以看作是一個獨立的執行環境,它包含了程序的代碼和相關的資源。每個進程都在自己的地址空間中運行,所以進程之間的數據隔離較好。進程之間通過進程間通信(IPC)機制來進行通信和共享數據。

線程是進程內的執行單位。一個進程可以包含多個線程,它們共享相同的地址空間和資源。線程擁有自己的程序計數器、寄存器集合和棧,但與同一進程的其他線程共享堆內存、全局變量和文件描述符等。

線程是輕量級的執行單位,它可以與其他線程併發執行,從而實現並行性。在多核處理器上,多個線程可以同時在不同的核上執行,提高系統的整體性能。

線程之間的切換比進程之間的切換更快,因為線程共享相同的上下文和資源。但也因為共享資源,線程之間需要注意同步和互斥,以避免數據競爭和併發問題。

總結來説,進程是獨立的執行環境,而線程是進程內的執行單位。進程之間相互獨立,線程之間共享相同的資源。進程之間切換開銷較大,線程之間切換開銷較小。進程和線程的合理使用可以提高程序的併發性和性能。

在瞭解了進程和線程後,我們再來了解阻塞和非阻塞的概念

阻塞、非阻塞 I/O 與同步、異步 I/O 的區別和聯繫

首先我們來看阻塞和非阻塞 I/O。根據應用程序是否阻塞自身運行,可以把 I/O 分為阻塞 I/O 和非阻塞 I/O。
所謂阻塞I/O,是指應用程序在執行I/O操作後,如果沒有獲得響應,就會阻塞當前線程,不能執行其他任務。
所謂非阻塞I/O,是指應用程序在執行I/O操作後,不會阻塞當前的線程,可以繼續執行其他的任務。
再來看同步 I/O 和異步 I/O。根據 I/O 響應的通知方式的不同,可以把文件 I/O 分為同步 I/O 和異步 I/O。
所謂同步 I/O,是指收到 I/O 請求後,系統不會立刻響應應用程序;等到處理完成,系統才會通過系統調用的方式,告訴應用程序 I/O 結果。
所謂異步 I/O,是指收到 I/O 請求後,系統會先告訴應用程序 I/O 請求已經收到,隨後再去異步處理;等處理完成後,系統再通過事件通知的方式,告訴應用程序結果。
你可以看出,阻塞/非阻塞和同步/異步,其實就是兩個不同角度的 I/O 劃分方式。它們描述的對象也不同,阻塞/非阻塞針對的是 I/O 調用者(即應用程序),而同步/異步針對的是 I/O 執行者(即系統)。

協程是什麼

首先協程的最簡定義是用户態線程,它不由操作系統而是由用户創建,跑在單個線程(核心)上,比進程或是線程都更加輕量化,通常創建它只有內存消耗:假如你的配置允許你開幾千個進程或線程,那麼開幾萬個幾十萬個協程也是很輕鬆的事情,只要內存足夠大,你可以幾乎無止境地創建新的協程。在Swoole下,協程的切換實現是依靠雙棧切換,即C棧和PHP棧同時切換,由於有棧協程的上下文總是足夠的小,且在用户態便能完成切換,它的切換速度也總是遠快於進程、線程,一般只需要納秒級的CPU時間,對於實際運行的邏輯代碼來説這點開銷總是可以忽略不計(尤其是在一個重IO的程序中,通過調用分析可以發現協程切換所佔的CPU時間非常之低)。

Go 語言中沒有線程的概念,只有協程,也稱為 goroutine。相比線程來説,協程更加輕量,一個程序可以隨意啓動成千上萬個 goroutine。

goroutine 被 Go runtime 所調度,這一點和線程不一樣。也就是説,Go 語言的併發是由 Go 自己所調度的,自己決定同時執行多少個 goroutine,什麼時候執行哪幾個。這些對於我們開發者來説完全透明,只需要在編碼的時候告訴 Go 語言要啓動幾個 goroutine,至於如何調度執行,我們不用關心

異步編程一般使用回調方式,如果遇到非常複雜的邏輯,可能會層層嵌套回調函數。協程就可以解決此問題,可以順序編寫代碼,但運行時是異步非阻塞的。騰訊的工程師基於Swoole擴展和PHP5.5的Yield/Generator語法實現類似於Golang的協程,項目名稱為TSF(Tencent Server Framework),開源項目地址:https://github.com/tencent-php/tsf。目前在騰訊公司的企業QQ、QQ公眾號項目以及車輪忽略的查違章項目有大規模應用 。

綜上所述:

所以來説,異步並不是物理上實現。計算機中只有進程和線程的概念。沒有協程的概念。我們
也可以把輕量級用户線程稱之為協程。那很多小明就要問了,既然不是物理上實現,那PHP、Java、Python 這些語言都會有異步操作為嘛就Go 熱度這麼高咧。這個就説來話長,從PHP 和 Python 這兩門腳本語言説起就一直爭論不休,對比之下再來看GO語言,就會發現使用爬蟲和大數據科學計算用python 語言,開發網站使用PHP語言,使用異步及協程使用Go 語言。這裏最直觀簡單的規律,語言已經封裝好了,python不用去單獨寫代碼實現,而PHP要寫代碼邏輯。go 語言只需要使用Go 關鍵字就可以開啓異步,其他語言得寫一大堆代碼,還容易出錯。還有一個在互聯網圈流行就是先入為主的關聯

異步

異步是指在程序執行過程中,某個操作不會阻塞後續代碼的執行。簡而言之,異步操作不會按照順序一步步執行,而是在後台進行,當操作完成時,通過回調函數、Promise對象、async/await等機制通知代碼繼續執行相關邏輯。

異步的優點

結合上面的介紹,既然異步的概念早就有了,為嘛直到GO問世後才人盡皆知咧。異步解決了問題是否是開發中的銀彈。

  1. 提高性能和響應速度
    異步編程允許程序執行其他任務而不是等待某個操作完成。這在處理 I/O 操作(例如網絡請求、文件讀寫)時尤為明顯。通過異步操作,主線程能夠繼續執行其他任務,提高了程序的性能和響應速度。
  2. 提升用户體驗
    在前端開發中,異步編程可用於處理用户交互和動畫,使得頁面更加流暢和響應。用户在進行操作時,頁面不會被阻塞,提升了整體用户體驗。

異步缺點

  1. 複雜性增加
    異步編程通常需要處理回調函數、Promise鏈、事件處理等複雜的代碼結構,容易導致代碼的可讀性和可維護性下降。特別是在回調嵌套較深的情況下,產生了所謂的 "回調地獄"。
  2. 錯誤處理複雜
    在異步編程中,錯誤的處理相對複雜,需要注意在回調鏈中適當地捕獲和處理錯誤,否則可能導致程序崩潰或難以調試。

問題一: 實驗異步能提高查詢MySQL的性能嗎?
答案:不能,異步是代碼邏輯實現的,查詢SQL語句是MySQL服務器查詢的。邏輯上代碼實現更服務器的查詢時間沒有關聯性。

MySQL異步 參考鏈接:MySQL的連接池、異步、斷線重連

MySQL異步是指將MySQL連接事件驅動化,這樣就變成了非阻塞IO。數據庫操作並不會阻塞進程,在MySQL-Server返回結果時再執行對應的邏輯。

有幾個點需要注意一下:

異步MySQL並沒有節省SQL執行的時間
一個MySQL連接同時只能執行1個SQL,如果異步MySQL存在併發那麼必須創建多個MySQL連接
異步回調程序中,異步MySQL並沒有提升性能。異步最大的好處是可以高併發,如果併發1萬個請求,那麼就需要建立1萬個MySQL連接,這會給MySQL-Server帶來巨大的壓力。

MySQL是根據連接數分配資源的,一個連接需要開啓一個線程。1000連接那麼需要維持1000線程才可以。線程數量增加後,線程間切換會佔用大量CPU資源
MySQL短連接反而不會出現此問題,因為短連接在使用完後就釋放了。不會佔用MySQL-Server的連接資源

雖然應用層代碼使用異步回調避免了自身的阻塞,實際上真正的瓶頸是數據庫服務器。異步MySQL還帶來了額外的編程複雜度,所以除非是特殊場景的需求,否則不建議使用異步MySQL。

如果程序中堅持要使用異步,那麼必須是異步MySQL+連接池的形式。超過規定的MySQL最大連接後,應當對SQL請求進行排隊,而不是創建新連接,避免大量併發請求導致MySQL服務器崩潰。

所以類似Redis、MySQL、Elasticsearch、Mogodb 使用異步能不能提高執行效率的問題同上。

拋開語言看問題,就好像 不識廬山真面目,只緣身在此山中

Add a new 評論

Some HTML is okay.