Go語言探索 - 12(結局)

來源:互聯網
上載者:User

上一篇文章文章主要學習了Go語言中的介面、反射以及錯誤和異常處理。本篇文章主要學習Go語言的協程,當然也是GO語言基礎的最後一篇。

goroutine:

goroutine是Go並行設計的核心,也是這門語言的精髓體現。goroutine這個關鍵字就是協程,但是它比線程更小。說起線程,大家可能都不陌生。線程,是程式執行的最小單元。一個標準的線程由線程ID,當前指令指標,寄存器集合和堆棧組成。另外,線程是進程中的一個實體,是被系統獨立調度和指派的基本單位,線程自己不擁有系統資源,只擁有一點兒在運行中必不可少的資源,但它可與同屬一個進程的其它線程共用進程所擁有的全部資源。

現在Go語言運用協程這一比線程更小的執行單元,十幾個goroutine可能體現在底層就是五六個線程,Go語言內部幫你實現了這些goroutine之間的記憶體共用。執行goroutine只需極少的棧記憶體(大概是4~5KB),當然會根據相應的資料伸縮。也正因為如此,可同時運行成千上萬個並發任務。goroutine比thread更易用、更高效、更輕便。

goroutine是通過Go的runtime管理的一個線程管理器。goroutine的作用就是一個普通的函數。以下是協程的寫法以及結果測試:

測試 - 1


測試 - 2


測試 - 3

理論上來說:多個goroutine運行在同一個進程裡面,共用記憶體資料,不過設計上我們要遵循:不要通過共用來通訊,而要通過通訊來共用。goroutine運行在相同的地址空間,因此訪問共用記憶體必須做好同步。那麼goroutine之間如何進行資料的通訊呢,Go提供了一個很好的通訊機制:channel。

channel是Go中的一個核心類型,你可以把它看成一個管道,通過它並發核心單元就可以發送或者接收資料進行通訊。

首先,channel必須先建立再使用,另外它的操作符是箭頭 <-

Channel類型的定義格式(有三種寫法)如下:

ChannelType = ( "chan" | "chan" "<-" | "<-" "chan" ) 資料類型

其中,這裡的 <- 代表的是channel方向。如果沒有指定方向,那麼Channel就是雙向,這樣就意味可以接收資料,也可以發送資料。

(前面說了可以把channel看成一個管道,既然是管道那麼就有流向)。也就是下面三種寫法:

chan T          // 可以接收和發送類型為 T 的資料

chan<- float64  // 只可以用來發送 float64 類型的資料

<-chan int      // 只可以用來接收 int 類型的資料

另外:

<-總是優先和最左邊的類型結合。以下是幾種組合寫法:

chan<- chan int    // 等價 chan<- (chan int)

chan<- <-chan int  // 等價 chan<- (<-chan int)

<-chan <-chan int  // 等價 <-chan (<-chan int)

容量(capacity):

使用make也可以初始化Channel,並且可以設定channel的容量:

這裡的容量可以這樣理解,就是channel可以儲存多少個元素,指定channel的緩衝大小、另外一個nil channel不會通訊。

寫法:

ch := make(chan type, value)

當 value = 0 時,也就說明channel 是無緩衝阻塞讀寫的;

當 value > 0 時,channel 有緩衝、是非阻塞的,直到寫滿 value 個元素才阻塞寫入。例如:

容量

以上代碼不變,當我們把容量設定為1,就會出現如下問題:

容量不足

Range和Close

range,這個關鍵字可以像操作slice或者map一樣,去操作緩衝類型的channel;

close,這個關鍵字主要是用來關閉channel。關閉channel之後就無法再發送任何資料。


rang

上面的代碼都是只有一個channel的情況,那麼如果存在多個channel的時候,我們該如何操作呢?

Go裡面提供了一個關鍵字select,通過select可以監聽channel上的資料流動。

它類似switch,但是只是用來處理通訊(communication)操作。它的case可以是send語句(發送),也可以是receive語句(接收),亦或者default。

default就是當監聽的channel都沒有準備好的時候,預設執行

select預設是阻塞的,只有當監聽的channel中有發送或接收可以進行時才會運行,當多個channel都準備好的時候,select是隨機的選擇一個執行的。

代碼如下:

func main() {

c := make(chan int)

quit := make(chan int)

go func() {

for i := 0; i < 3; i++ {

fmt.Println("<-c == ",<-c)

}

quit <- 0

}()

testSelect(c, quit)

}

func testSelect(c, quit chan int) {

x, y := 1, 1

for {

select {

case c <- x:

x, y = y, x + y

fmt.Println("x == ",x)

fmt.Println("y+x == ",y+x)

case <- quit:

fmt.Println("quit")

return

}

}

}

逾時處理:timeout

select有很重要的一個應用就是逾時處理。如果select中沒有case需要處理,select語句就會一直阻塞著。

這時候我們可能就需要一個逾時操作,用來處理逾時以避免整個程式進入阻塞。Go語言的逾時處理是使用的timeout關鍵字

以下是複現逾時的例子,我分了兩種情況:

下面是第二種,複現逾時的情景:

複現逾時

下面是收集了一些runtime包中關於處理goroutine的幾個函數:

A: Goexit 這個函數的意思指:退出當前執行的goroutine,但是defer函數還會繼續調用

B: Gosched 這個函數的意思指:讓出當前goroutine的執行許可權,調度器安排其他等待的任務運行,並在下次某個時候從該位置恢複執行。

C: NumCPU 這個函數的意思指:返回 CPU 核心數量

D: NumGoroutine 這個函數的意思指:返回正在執行和排隊的任務總數

E: GOMAXPROCS 這個函數的意思指:用來設定可以並行計算的CPU核心數的最大值,並返回之前的值。

結語:

關於Go語言的基礎內容,基本上就寫完了。也算是這一階段的學習筆記與總結。

GoLang在學習的過程中,個人最深刻的體會就是對記憶體的超嚴格控制管理、文法簡潔精悍、更小更精準的協程執行單元、函數多傳回值等等特點。

雖然網上對這麼語言褒貶不一,但還是希望這門語言可以發展的越來越好。

如果這篇文章對你有協助,希望各位看官留下寶貴的star,謝謝。

Ps:著作權歸作者所有,轉載請註明作者, 商業轉載請聯絡作者獲得授權,非商業轉載請註明出處(開頭或結尾請添加轉載出處,添加原文url地址),文章請勿濫用,也希望大家尊重筆者的勞動成果

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.