當前位置:首頁 » 服務存儲 » gochannel存儲對象
擴展閱讀
webinf下怎麼引入js 2023-08-31 21:54:13
堡壘機怎麼打開web 2023-08-31 21:54:11

gochannel存儲對象

發布時間: 2022-11-13 01:20:19

1. Go中的make和new的區別

·new:是一個用來分配內存的內置函數,與C++不同的是,它不初始化內存,只是將其歸零,也就相當於,new(X)會為X的新項目分配被歸零的存儲,且返回它的地址,其中,第一個參數是類型,返回值是類型的指針,其值被初始化為『0』,對於不同的數據類型,0值的意義也是不一樣的,比如int初始化為0,bool初始化為false等等。
·make:是Golang的內置函數,僅用於分配和初始化slice、map及channel類型的對象,三種類型都是結構,返回值為類型而不是指針,例如slice是一個三元描述符,包含一個指向數據(在數組中)的指針,長度以及容量,在這些項被初始化前,slice都是nil的,對於這三者,make初始化這些內部數據結構,並准備好可用的值。
需要注意的是,make只用於map、slice和channel,並且不反悔指針,想要獲得一個顯式的指針,使用new進行分配,或者顯式地使用一個變數的地址。

2. 【golang詳解】go語言GMP(GPM)原理和調度

Goroutine調度是一個很復雜的機制,下面嘗試用簡單的語言描述一下Goroutine調度機制,想要對其有更深入的了解可以去研讀一下源碼。

首先介紹一下GMP什麼意思:

G ----------- goroutine: 即Go協程,每個go關鍵字都會創建一個協程。

M ---------- thread內核級線程,所有的G都要放在M上才能運行。

P ----------- processor處理器,調度G到M上,其維護了一個隊列,存儲了所有需要它來調度的G。

Goroutine 調度器P和 OS 調度器是通過 M 結合起來的,每個 M 都代表了 1 個內核線程,OS 調度器負責把內核線程分配到 CPU 的核上執行

模型圖:

避免頻繁的創建、銷毀線程,而是對線程的復用。

1)work stealing機制

  當本線程無可運行的G時,嘗試從其他線程綁定的P偷取G,而不是銷毀線程。

2)hand off機制

  當本線程M0因為G0進行系統調用阻塞時,線程釋放綁定的P,把P轉移給其他空閑的線程執行。進而某個空閑的M1獲取P,繼續執行P隊列中剩下的G。而M0由於陷入系統調用而進被阻塞,M1接替M0的工作,只要P不空閑,就可以保證充分利用CPU。M1的來源有可能是M的緩存池,也可能是新建的。當G0系統調用結束後,根據M0是否能獲取到P,將會將G0做不同的處理:

如果有空閑的P,則獲取一個P,繼續執行G0。

如果沒有空閑的P,則將G0放入全局隊列,等待被其他的P調度。然後M0將進入緩存池睡眠。

如下圖

GOMAXPROCS設置P的數量,最多有GOMAXPROCS個線程分布在多個CPU上同時運行

在Go中一個goroutine最多佔用CPU 10ms,防止其他goroutine被餓死。

具體可以去看另一篇文章

【Golang詳解】go語言調度機制 搶占式調度

當創建一個新的G之後優先加入本地隊列,如果本地隊列滿了,會將本地隊列的G移動到全局隊列裡面,當M執行work stealing從其他P偷不到G時,它可以從全局G隊列獲取G。

協程經歷過程

我們創建一個協程 go func()經歷過程如下圖:

說明:

這里有兩個存儲G的隊列,一個是局部調度器P的本地隊列、一個是全局G隊列。新創建的G會先保存在P的本地隊列中,如果P的本地隊列已經滿了就會保存在全局的隊列中;處理器本地隊列是一個使用數組構成的環形鏈表,它最多可以存儲 256 個待執行任務。

G只能運行在M中,一個M必須持有一個P,M與P是1:1的關系。M會從P的本地隊列彈出一個可執行狀態的G來執行,如果P的本地隊列為空,就會想其他的MP組合偷取一個可執行的G來執行;

一個M調度G執行的過程是一個循環機制;會一直從本地隊列或全局隊列中獲取G

上面說到P的個數默認等於CPU核數,每個M必須持有一個P才可以執行G,一般情況下M的個數會略大於P的個數,這多出來的M將會在G產生系統調用時發揮作用。類似線程池,Go也提供一個M的池子,需要時從池子中獲取,用完放回池子,不夠用時就再創建一個。

work-stealing調度演算法:當M執行完了當前P的本地隊列隊列里的所有G後,P也不會就這么在那躺屍啥都不幹,它會先嘗試從全局隊列隊列尋找G來執行,如果全局隊列為空,它會隨機挑選另外一個P,從它的隊列里中拿走一半的G到自己的隊列中執行。

如果一切正常,調度器會以上述的那種方式順暢地運行,但這個世界沒這么美好,總有意外發生,以下分析goroutine在兩種例外情況下的行為。

Go runtime會在下面的goroutine被阻塞的情況下運行另外一個goroutine:

用戶態阻塞/喚醒

當goroutine因為channel操作或者network I/O而阻塞時(實際上golang已經用netpoller實現了goroutine網路I/O阻塞不會導致M被阻塞,僅阻塞G,這里僅僅是舉個栗子),對應的G會被放置到某個wait隊列(如channel的waitq),該G的狀態由_Gruning變為_Gwaitting,而M會跳過該G嘗試獲取並執行下一個G,如果此時沒有可運行的G供M運行,那麼M將解綁P,並進入sleep狀態;當阻塞的G被另一端的G2喚醒時(比如channel的可讀/寫通知),G被標記為,嘗試加入G2所在P的runnext(runnext是線程下一個需要執行的 Goroutine。), 然後再是P的本地隊列和全局隊列。

系統調用阻塞

當M執行某一個G時候如果發生了阻塞操作,M會阻塞,如果當前有一些G在執行,調度器會把這個線程M從P中摘除,然後再創建一個新的操作系統的線程(如果有空閑的線程可用就復用空閑線程)來服務於這個P。當M系統調用結束時候,這個G會嘗試獲取一個空閑的P執行,並放入到這個P的本地隊列。如果獲取不到P,那麼這個線程M變成休眠狀態, 加入到空閑線程中,然後這個G會被放入全局隊列中。

隊列輪轉

可見每個P維護著一個包含G的隊列,不考慮G進入系統調用或IO操作的情況下,P周期性的將G調度到M中執行,執行一小段時間,將上下文保存下來,然後將G放到隊列尾部,然後從隊列中重新取出一個G進行調度。

除了每個P維護的G隊列以外,還有一個全局的隊列,每個P會周期性地查看全局隊列中是否有G待運行並將其調度到M中執行,全局隊列中G的來源,主要有從系統調用中恢復的G。之所以P會周期性地查看全局隊列,也是為了防止全局隊列中的G被餓死。

除了每個P維護的G隊列以外,還有一個全局的隊列,每個P會周期性地查看全局隊列中是否有G待運行並將其調度到M中執行,全局隊列中G的來源,主要有從系統調用中恢復的G。之所以P會周期性地查看全局隊列,也是為了防止全局隊列中的G被餓死。

M0

M0是啟動程序後的編號為0的主線程,這個M對應的實例會在全局變數rutime.m0中,不需要在heap上分配,M0負責執行初始化操作和啟動第一個G,在之後M0就和其他的M一樣了

G0

G0是每次啟動一個M都會第一個創建的goroutine,G0僅用於負責調度G,G0不指向任何可執行的函數,每個M都會有一個自己的G0,在調度或系統調用時會使用G0的棧空間,全局變數的G0是M0的G0

一個G由於調度被中斷,此後如何恢復?

中斷的時候將寄存器里的棧信息,保存到自己的G對象裡面。當再次輪到自己執行時,將自己保存的棧信息復制到寄存器裡面,這樣就接著上次之後運行了。

我這里只是根據自己的理解進行了簡單的介紹,想要詳細了解有關GMP的底層原理可以去看Go調度器 G-P-M 模型的設計者的文檔或直接看源碼

參考: (https://www.cnblogs.com/X-knight/p/11365929.html)

(https://draveness.me/golang/docs/part3-runtime/ch06-concurrency/golang-goroutine/)

3. 什麼是對象存儲

什麼是對象存儲?

存儲區域網(SAN)和網路附加存儲(NAS)是我們比較熟悉的兩種主流網路存儲架構,而對象存儲(Object-based Storage)是一種新的網路存儲架構,基於對象存儲技術的設備就是對象存儲設備(Object-based Storage Device)簡稱OSD。

對象存儲的發展歷史:

1999年成立的全球網路存儲工業協會(SNIA)的對象存儲設備(Object Storage Device)工作組發布了ANSI的X3T10標准。

對象存儲的優點:

總體上來講,對象存儲同兼具SAN高速直接訪問磁碟特點及NAS的分布式共享特點。

SAN(Storage Area Network)結構

採用SCSI 塊I/O的命令集,通過在磁碟或FC(Fiber Channel)級的數據訪問提供高性能的隨機I/O和數據吞吐率,它具有高帶寬、低延遲的優勢,在高性能計算中佔有一席之地,如SGI的CXFS文件系統就是基於SAN實現高性能文件存儲的,但是由於SAN系統的價格較高,且可擴展性較差,已不能滿足成千上萬個CPU規模的系統。

4. Go 語言 channel 的阻塞問題

Hello,大家好,又見面了!上一遍我們將 channel 相關基礎以及使用場景。這一篇,還需要再次進階理解channel 阻塞問題。以下創建一個chan類型為int,cap 為3。

channel 內部其實是一個環形buf數據結構 ,是一種滑動窗口機制,當make完後,就分配在 Heap 上。

上面,向 chan 發送一條「hello」數據:

如果 G1 發送數據超過指定cap時,會出現什麼情況?

看下面實例:

以上會出現什麼,chan 緩沖區允許大小為1,如果再往chan仍數據,滿了就會被阻塞,那麼是如何實現阻塞的呢?當 chan 滿時,會進入 gopark,此時 G1 進入一個 waiting 狀態,然後會創建一個 sudog 對象,其實就sendq隊列,把 200放進去。等 buf 不滿的時候,再喚醒放入buf裡面。

通過如下源碼,你會更加清晰:

上面,從 chan 獲取數據:

Go 語言核心思想:「Do not communicate by sharing memory; instead, share memory by communicating.」 你可以看看這本書名叫:Effective Go

如果接收者,接收一個空對象,也會發生什麼情況?

代碼示例

也會報錯如下:

上面,從 chan 取出數據,可是沒有數據了。此時,它會把 接收者 G2 阻塞掉,也是和G1發送者一樣,也會執行 gopark 將狀態改為 waiting,不一樣的點就是。

正常情況下,接收者G2作為取出數據是去 buf 讀取數據的,但現在,buf 為空了,此時,接收者G2會將sudog導出來,因為現在G2已經被阻塞了嘛,會把G2給G,然後將 t := <-ch 中變數 t 是在棧上的地址,放進去 elem ,也就是說,只存它的地址指針在sudog裡面。

最後, ch <- 200 當G1往 chan 添加200這個數據,正常情況是將數據添加到buf裡面,然後喚醒 G2 是吧,而現在是將 G1 的添加200數據直接干到剛才G2阻塞的t這里變數裡面。

你會認為,這樣真的可以嗎?想一想,G2 本來就是已經阻塞了,然後我們直接這么干肯定沒有什麼毛病,而且效率提高了,不需要再次放入buf再取出,這個過程也是需要時間。不然,不得往chan添加數據需要加鎖、拷貝、解鎖一序列操作,那肯定就慢了,我想Go語言是為了高效及內存使用率的考慮這樣設計的。(注意,一般都是在runtime裡面完成,不然會出現象安全問題。)

總結

chan 類型的特點:chan 如果為空,receiver 接收數據的時候就會阻塞等待,直到 chan 被關閉或者有新的數據到來。有這種個機制,就可以實現 wait/notify 的設計模式。

相關面試題:



5. go語言中channel的問題

第一個問題,打開文件應添加"|os.O_WRONLY"

file, err := os.OpenFile("data.dat",os.O_CREATE|os.O_APPEND|os.O_WRONLY,0777)

第二個問題,將Count方法中的 "ch<-i"放到方法的最後一行就可以了。
因為一旦「ch<-i"執行了,main方法中的 "<-ch"就會執行通過當所有的"<-ch"執行完後程序就結束了。但這時Count的線程方法還來不及執行完(打開文件的速度相對較慢),所以僅僅執行一次文件操作就結束了

6. 求 分布式對象存儲 原理 架構及Go語言實現 pdf

分布式存儲架構由三個部分組成:客戶端、元數據伺服器和數據伺服器。客戶端負責發送讀寫請求,緩存文件元數據和文件數據。元數據伺服器負責管理元數據和處理客戶端的請求,是整個系統的核心組件。數據伺服器負責存放文件數據,保證數據的可用性和完整性。該架構的好處是性能和容量能夠同時拓展,系統規模具有很強的伸縮性。
對象存儲最常用的方案,就是多台伺服器內置大容量硬碟,再裝上對象存儲軟體,然後再額外搞幾台服務作為管理節點,安裝上對象存儲管理軟體。管理節點可以管理其他伺服器對外提供讀寫訪問功能。
之所以出現了對象存儲這種東西,是為了克服塊存儲與文件存儲各自的缺點,發揚它倆各自的優點。簡單來說塊存儲讀寫快,不利於共享,文件存儲讀寫慢,利於共享。能否弄一個讀寫快,利 於共享的出來呢。於是就有了對象存儲。

7. 什麼是對象存儲

對象存儲的發展歷史: 1999年成立的全球網路存儲工業協會(SNIA)的對象存儲設備(Object Storage Device)工作組發布了ANSI的X3T10標准。 對象存儲的優點: 總體上來講,對象存儲同兼具SAN高速直接訪問磁碟特點及NAS的分布式共享特點。 SAN(Storage Area Network)結構 採用SCSI 塊I/O的命令集,通過在磁碟或FC(Fiber Channel)級的數據訪問提供高性能的隨機I/O和數據吞吐率,它具有高帶寬、低延遲的優勢,在高性能計算中佔有一席之地,如SGI的CXFS文件系統就是基於SAN實現高性能文件存儲的,但是由於SAN系統的價格較高,且可擴展性較差,已不能滿足成千上萬個CPU規模的系統。 來自中國存儲網 ChinaStor.com NAS(Network Attached Storage)結構 它採用NFS或CIFS命令集訪問數據,以文件為傳輸協議,通過TCP/IP實現網路化存儲,可擴展性好、價格便宜、用戶易管理,如目前在集群計算中應用較多的NFS文件系統,但由於NAS的協議開銷高、帶寬低、延遲大,不利於在高性能集群中應用。 對象存儲結構 核心是將數據通路(數據讀或寫)和控制通路(元數據)分離,並且基於對象存儲設備(Object-based Storage Device,OSD)構建存儲系統,每個對象存儲設備具有一定的智能,能夠自動管理其上的數據分布。

8. go語言對象的問題

Get轉到定義是如下代碼,
func (c *Client) Get(url string) (resp *Response, err error) {
req, err := NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
return c.Do(req)
}

看上去已經有足夠多的動作了,並不是你說的只是一個介面啊

9. Golang入門到項目實戰 | golang並發變成之通道channel

Go提供了一種稱為通道的機制,用於在goroutine之間共享數據。當您作為goroutine執行並發活動時,需要在goroutine之間共享資源或數據,通道充當goroutine之間的管道(管道)並提供一種機制來保證同步交換。

根據數據交換的行為,有兩種類型的通道:無緩沖通道和緩沖通道。無緩沖通道用於執行goroutine之間的同步通信,而緩沖通道用於執行非同步通信。無緩沖通道保證在發送和接收發生的瞬間兩個goroutine之間的交換。緩沖通道沒有這樣的保證。

通道由make函數創建,該函數指定chan關鍵字和通道的元素類型。

這是創建無緩沖和緩沖通道的代碼塊:

語法

使用內置函數make創建無緩沖和緩沖通道。make的第一個參數需要關鍵字chan,然後是通道允許交換的數據類型。

這是將值發送到通道的代碼塊需要使用<-運算符:

語法

一個包含5個值的緩沖區的字元串類型的goroutine1通道。然後我們通過通道發送字元串「Australia」。

這是從通道接收值的代碼塊:

語法

<- 運算符附加到通道變數(goroutine1)的左側,以接收來自通道的值。

在無緩沖通道中,在接收到任何值之前沒有能力保存它。在這種類型的通道中,發送和接收goroutine在任何發送或接收操作完成之前的同一時刻都准備就緒。如果兩個goroutine沒有在同一時刻准備好,則通道會讓執行其各自發送或接收操作的goroutine首先等待。同步是通道上發送和接收之間交互的基礎。沒有另一個就不可能發生。

在緩沖通道中,有能力在接收到一個或多個值之前保存它們。在這種類型的通道中,不要強制goroutine在同一時刻准備好執行發送和接收。當發送和接收阻塞時也有不同的條件。只有當通道中沒有要接收的值時,接收才會阻塞。僅當沒有可用緩沖區來放置正在發送的值時,發送才會阻塞。

實例

運行結果

10. 2020-10-23:go中channel的創建流程是什麼

使用net包,連接客戶端,調用dial介面或者其他類似介面,建立連接,連接服務端調用listen介面或者其他類似介面,具體調用參數查看介麵包內介面注釋