【go語言】Goroutines 併發模式(一)

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

前言

由於前一階段實習中接到的項目的告一段落,不知不覺便多出了許多空餘的時間,於是就想總結一下最近因為個人興趣而學習的一些東西。從這篇文章開始以及後面陸續的幾篇關於GO語言的文章,均是博主最近對GO語言學習過程中的一些感悟、總結,類似於學習筆記的東西。記錄下來並整理成部落格一為對學習的知識做一個整理,二為分享出來給大家(因為國內關於GO語言的中文資料比較少),由於博主能力和知識有限,難免有所靡誤,還望勘正。

由於Go最近一系列出色的表現,從一開始Go便緊緊地吸引住了我的眼球。類似於Erlang、Scala等語言,Go也是天生為並發而設計的語言,Go有著許多在原生層面對並發編程進行支援的優秀特性,比如大名鼎鼎的Goroutines、Channels、Select等原生特性。那麼廢話不多說,這一篇主要是對GO語言中的並發編程模式做一個粗略的歸納總結,文中樣本參考自golang conference中的一些演講和部落格,涉及到的Go語言的文法知識細節將予以略去。Go語言文法請參考http://golang.org/


幾點強調之處

1. 並發而非並行

首先我們要明確兩個名詞:並發(Concurrency)、並行(Parallelism)。這兩個詞可能大家經常搞混淆,因為這兩個詞所標書的意思太過相近,但是前者更加偏向於設計(Design),而後者更加偏向於結構(Structure)。

  • 如果你有只有一個CPU,那麼你的程式可以是並發的,但一定不是並行的

  • 一個良好的並發程式並非一定是並行的

  • 並行是一種物理狀態,而並發是一種設計思想、程式的內部結構

  • 多處理器才有可能達到並發的物理狀態

2. 什麼是Goroutines

Goroutine是一個通過go關鍵字起起來的獨立的執行某個function的過程,它擁有獨立的可以自行管理的調用棧。

  • goroutine非常廉價,你可以擁有幾千甚至上萬的goroutines

  • goroutine不是thread

  • 一個thread之下可能有上千的goroutines

  • 你可以把goroutine理解為廉價的thread


讓我們從幾個例子開始

  • 一個很無聊的函數

func boring(msg string) {    for i := 0; ; i++ {       fmt.Println(msg, i)       time.Sleep(time.Second)    }}

顯而易見,這個函數永不停歇的列印msg字串,並且迴圈中間會sleep一秒鐘,接下來讓我們不斷改進這個函數。


  • 嗯哼,稍微不那麼無聊一點了

func boring(msg string) {    for i := 0; ; i++ {       fmt.Println(msg, i)       time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)    }}

我們看到這個函數不再是sleep固定的時間,而是rand出一個隨機的duration。這樣,可以讓我們的這個無聊的函數稍微不可預期一點。

  • 讓我們把它run起來!~Let's go!

func main() {    boring("boring!")}func boring(msg string) {    for i := 0; ; i++ {        fmt.Println(msg, i)        time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)    }}

好了,無聊的函數跑起來了~~但是,目前我們還沒有用到Goroutines的特性

  • 讓函數Go起來!

package mainimport (    "fmt"    "math/rand"    "time")func main() {go boring("boring!")}

程式的輸出為:

[no output]Program exited.

納尼??!!奇怪啊,為什麼程式沒有輸出捏?其實真相是,main函數在開啟boring方法的新的goroutine之後,沒有等待boring方法調用fmt.Println就急急忙忙返回退出了。當main退出之時,我們的程式自然而然也就退出了。

  • 讓我們等一下TA吧

func main() {go boring("boring!")fmt.Println("I'm listening.")time.Sleep(2 * time.Second)fmt.Println("You're boring; I'm leaving.")}

現在我們就可以在主程式退出之前看到boring函數輸出的message了。但是等等,我們現在還只是一個goroutine,還沒有涉及到真正意義上的並發。

  • 使用channels!

讓我們先來看一個簡單的使用channels進行同步的例子

var syn chan int = make(chan int)func foo(){for(i := 0; i < 5; i++){fmt.Println("i am running!")}syn <- 1}func main(){go foo()<- syn}

很簡單吧,通過使用通道syn,可以進行簡單的同步。這樣,在main函數退出之間首先會在讀取syn處阻塞,除非foo向syn寫入資料。

  • 讓boring和main成為好基友

func boring(msg string, c chan string) {   for i := 0; ; i++ {        c <- fmt.Sprintf("%s %d", msg, i)        time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)}}func main() {c := make(chan string)go boring("boring!", c) for i := 0; i < 5; i++ {    fmt.Printf("You say: %q\n", <-c)    }    fmt.Println("You're boring; I'm leaving.")}

我們通過channel將main和boring聯絡起來,從而讓本來毫無關係的他們能夠自然地交流,從而知曉彼此的狀態。上面程式便是通過channel來進行的同步。當main函數執行 "<- c"時會發生阻塞,除非boring中執行"c <- fmt.Sprintf("%s %d", msg, i)"向通道中寫入資料才會解除阻塞。由此觀之,即針對同一個channel,sender和receiver必須要一個讀一個寫才能使得channel暢通不阻塞。如此一來,便可以通過channel進行交流和同步。






相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.