并發(fā)編程是當(dāng)前軟件領(lǐng)域中不可忽視的一個(gè)關(guān)鍵概念。隨著CPU等硬件的不斷發(fā)展,我們都渴望讓我們的程序運(yùn)行速度更快、更快。而Go語(yǔ)言在語(yǔ)言層面天生支持并發(fā),充分利用現(xiàn)代CPU的多核優(yōu)勢(shì),這也是Go語(yǔ)言能夠廣泛流行的一個(gè)重要原因。
在Java中,要支持高并發(fā)有幾種方案可供選擇。首先,我們可以通過(guò)開(kāi)啟多部署節(jié)點(diǎn)集群來(lái)增加高并發(fā)處理能力,通過(guò)增加機(jī)器硬件來(lái)實(shí)現(xiàn)。其次,我們可以在單節(jié)點(diǎn)上開(kāi)啟多線程來(lái)處理請(qǐng)求。然而,即使在單節(jié)點(diǎn)內(nèi)創(chuàng)建線程也是非常耗費(fèi)資源的。因此,通常情況下我們會(huì)使用線程池來(lái)管理線程的創(chuàng)建和銷毀。然而,有一個(gè)公式你可能會(huì)很熟悉,即核心線程數(shù)等于CPU核數(shù)的一半加一。這意味著我們并不是線程創(chuàng)建得越多,對(duì)于我們的Java程序就越好。
在我們明確了問(wèn)題的痛點(diǎn)之后,我們可以進(jìn)一步探究一下Go語(yǔ)言是如何解決這些問(wèn)題,并且將高并發(fā)作為Go語(yǔ)言的一項(xiàng)特色功能。
我們?cè)贘ava中開(kāi)啟線程的方式是直接創(chuàng)建一個(gè)Thread對(duì)象。然而,在Go語(yǔ)言中,如果我們想要實(shí)現(xiàn)異步處理,我們可以使用"go"關(guān)鍵字來(lái)開(kāi)啟一個(gè)goroutine協(xié)程。協(xié)程的最大優(yōu)勢(shì)在于其輕量級(jí),可以輕松創(chuàng)建上百萬(wàn)個(gè)協(xié)程而不會(huì)導(dǎo)致系統(tǒng)資源的耗盡,而線程和進(jìn)程通常最多也不能超過(guò)1萬(wàn)個(gè)。舉個(gè)例子:
go f() // 創(chuàng)建一個(gè)新的 goroutine 運(yùn)行函數(shù)f
在Go語(yǔ)言中,我們可以非常簡(jiǎn)單地使用關(guān)鍵字"go"來(lái)開(kāi)啟一個(gè)協(xié)程,從而實(shí)現(xiàn)異步處理函數(shù)f。只需在函數(shù)f的調(diào)用前面加上"go"關(guān)鍵字,就能使得該函數(shù)在一個(gè)獨(dú)立的協(xié)程中異步執(zhí)行。
不僅可以使用"go"關(guān)鍵字來(lái)開(kāi)啟一個(gè)協(xié)程異步執(zhí)行具名函數(shù),還可以使用"go"關(guān)鍵字來(lái)開(kāi)啟一個(gè)協(xié)程異步執(zhí)行匿名函數(shù)。
go func(){ // ...}()
今天我們的重點(diǎn)不在這里,而是要討論為什么Go語(yǔ)言適合處理高并發(fā)的情況。我們都知道,操作系統(tǒng)的CPU最小調(diào)度單位是線程,然而Go語(yǔ)言卻使用了協(xié)程的概念。那么問(wèn)題來(lái)了,Go語(yǔ)言是如何將這些協(xié)程交給CPU來(lái)處理的呢?如果無(wú)法將它們交給CPU處理,那么就算再創(chuàng)建多少協(xié)程也無(wú)法運(yùn)行代碼。在這里,我們就需要了解一下Go語(yǔ)言的調(diào)度器,也就是GPM調(diào)度模型。
可以借鑒一下以下圖例,總的來(lái)說(shuō),我們可以像線程池一樣,無(wú)論創(chuàng)建了多少協(xié)程,都需要將它們放入隊(duì)列中。然后,剩下的任務(wù)就交給調(diào)度器來(lái)處理。
圖片
其中:
Goroutine 調(diào)度器和操作系統(tǒng)調(diào)度器通過(guò) M 結(jié)合起來(lái),形成了調(diào)度的基本單位。在這個(gè)結(jié)合中,每個(gè) M 代表一個(gè)內(nèi)核線程,而操作系統(tǒng)調(diào)度器則負(fù)責(zé)將這些內(nèi)核線程分配到 CPU 的核心上進(jìn)行執(zhí)行。
單純地將函數(shù)并發(fā)執(zhí)行是沒(méi)有意義的,因?yàn)楹瘮?shù)與函數(shù)之間需要進(jìn)行數(shù)據(jù)交換,才能真正體現(xiàn)并發(fā)執(zhí)行函數(shù)的意義。
雖然可以利用共享內(nèi)存進(jìn)行數(shù)據(jù)交換,但是在不同的 goroutine 中使用共享內(nèi)存容易導(dǎo)致競(jìng)態(tài)問(wèn)題的出現(xiàn)。為了確保數(shù)據(jù)交換的正確性,許多并發(fā)模型都需要通過(guò)使用互斥量對(duì)內(nèi)存進(jìn)行加鎖來(lái)解決這個(gè)問(wèn)題。然而,這種做法往往會(huì)帶來(lái)性能問(wèn)題,因?yàn)榧渔i操作會(huì)引入額外的開(kāi)銷。
Go語(yǔ)言采用的并發(fā)模型是CSP(Communicating Sequential Processes),這個(gè)模型強(qiáng)調(diào)了通過(guò)通信共享內(nèi)存的方式來(lái)實(shí)現(xiàn)并發(fā),而不是通過(guò)共享內(nèi)存來(lái)實(shí)現(xiàn)通信。這種設(shè)計(jì)理念使得Go語(yǔ)言在處理并發(fā)任務(wù)時(shí)更加高效和安全。
如果說(shuō) goroutine 是Go程序中實(shí)現(xiàn)并發(fā)執(zhí)行的主體,那么channel就是連接這些goroutine之間的紐帶。channel是一種能夠使得一個(gè)goroutine向另一個(gè)goroutine發(fā)送特定值的通信機(jī)制。
Mutex(互斥鎖)在實(shí)現(xiàn)上也是使用了重量級(jí)鎖。與Java的互斥鎖相比,Go語(yǔ)言的Mutex有以下幾點(diǎn)區(qū)別:
內(nèi)存開(kāi)銷:Go語(yǔ)言的Mutex相對(duì)較輕量,使用較少的內(nèi)存。這是因?yàn)镚o語(yǔ)言的Mutex只包含一個(gè)字段,用于表示鎖的狀態(tài),而Java的互斥鎖通常包含更多的字段和數(shù)據(jù)結(jié)構(gòu)。
鎖的語(yǔ)法:在Go語(yǔ)言中,可以使用mutex.Lock()和mutex.Unlock()方法來(lái)手動(dòng)控制鎖的獲取和釋放,這樣可以更靈活地控制鎖的粒度。而在Java中,使用synchronized關(guān)鍵字來(lái)實(shí)現(xiàn)互斥鎖,鎖的粒度相對(duì)固定,只能對(duì)整個(gè)方法或代碼塊進(jìn)行加鎖。
鎖的性能:由于Go語(yǔ)言的Mutex較為輕量,并且采用了更高效的實(shí)現(xiàn)方式,比如以下幾個(gè)方面:
并發(fā)編程是當(dāng)前軟件領(lǐng)域中一個(gè)重要的概念。Go語(yǔ)言通過(guò)goroutine和channel的特性,天生支持高并發(fā)處理,充分利用現(xiàn)代CPU的多核優(yōu)勢(shì)。與Java相比,Go語(yǔ)言的協(xié)程更加輕量級(jí),可以輕松創(chuàng)建上百萬(wàn)個(gè)協(xié)程。Go語(yǔ)言的調(diào)度器采用GPM調(diào)度模型,通過(guò)將協(xié)程放入隊(duì)列中,由調(diào)度器分配給CPU處理。此外,Go語(yǔ)言采用CSP模型,通過(guò)channel實(shí)現(xiàn)協(xié)程之間的通信,避免了共享內(nèi)存帶來(lái)的競(jìng)態(tài)問(wèn)題。相比之下,Go語(yǔ)言的Mutex鎖更輕量、靈活,并且具有更高的性能。總的來(lái)說(shuō),Go語(yǔ)言適合處理高并發(fā)的情況,成為了當(dāng)前軟件開(kāi)發(fā)領(lǐng)域的熱門語(yǔ)言之一。
本文鏈接:http://www.www897cc.com/showinfo-26-55255-0.html你是否想知道如何應(yīng)對(duì)高并發(fā)?Go語(yǔ)言為你提供了答案!
聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com