1. 程式人生 > >【鏈塊技術35期】區塊鏈技術語言——Go語言併發程式設計(上)

【鏈塊技術35期】區塊鏈技術語言——Go語言併發程式設計(上)

併發程式設計分為上、下兩節。這一節包括了併發程式設計的概述、goroutine和channel的部分內容。

一、概述

1.1 並行和併發

並行(parallel):在多個處理器上同時執行多條指令,如圖1所示。

併發(concurrency):同一時刻只有一條指令在執行,但多個程序指令被快速輪換地執行,使得巨集觀上具有多個程序同時執行的效果,但在微觀上並不是同時執行的。它只是把這段時間進行劃分、快速交替地執行,如圖2所示。

1.2 Go語言併發優勢

Go語言原生支援併發。

首先,雖然一個併發程式記憶體管理複雜,但Go語言提供了自動垃圾回收機制,可以自動判斷何時需要釋放記憶體,並在CPU相對空閒的時候對不使用的記憶體進行收集。

其次,Go語言提供了goroutine。goroutine即一個函式或方法,它僅需4~5KB的記憶體,所以建立一個goroutine代價極小。另外,Go語言存在一個內建的資料結構——通道(channel),它能夠讓不同的goroutine之間同步安全地傳送訊息。因此,Go語言讓併發程式設計變得更加輕盈和安全。

因此,一個執行的程式可以建立成百上千個goroutine,充分利用計算機資源,自動管理記憶體,實現了高併發。

二、goroutine

2.1 goroutine的定義

關於goroutine,Go語言之父Rob Pike說:“一個goroutine是一個與其它goroutines併發執行在同一地址空間的Go函式或方法。一個執行的程式由一個或更多個goroutine組成。它與執行緒、協程、程序等不同,它是一個goroutine。”

所以我們可以認為goroutine就是一個函式或方法。

2.2 goroutine的建立和執行

在函式或者方法的呼叫語句之前新增關鍵字go,就可以建立一個goroutine。開發人員無需瞭解任何執行細節,排程器會自動將其安排到合適的系統執行緒上執行。

當一個程式啟動時,其主函式在一個單獨的goroutine中執行,這個goroutine叫作main goroutine。新的goroutine用go語句來建立。

2.2.1 main goroutine

在Go語言裡,主函式執行在main goroutine中,其它goroutines和main goroutine併發執行。如果main goroutine先執行完畢,那麼其它的goroutines也會自動退出。

2.2.2 其它goroutines

如果要執行所有其它的goroutines,main goroutine必須繼續在執行,直到其它goroutines執行完畢。

2.3 runtime包

2.3.1 Gosched

runtime.Gosched() 用於讓出當前goroutine的執行許可權,排程器安排其他等待的任務執行,並在下次某個時候從該位置恢復執行。

2.3.2 Goexit

呼叫runtime.Goexit(),會立即終止當前goroutine的執行,排程器確保所有已註冊defer延遲呼叫被執行。

2.3.3 GOMAXPROCS

呼叫runtime.GOMAXPROCS() 用來設定可以平行計算的CPU核數的最大值,並且返回設定之前用於平行計算的CPU核數最大值。

三、channel

在Go語言裡,各個goroutine執行在相同的地址空間,因此訪問共享記憶體必須做好同步。

當一個資源需要在兩個goroutine之間共享,channel在兩個goroutine之間架起一個管道,並提供了確保同步交換資料的機制。可以通過channel共享內建型別、命名型別、結構型別和引用型別的值或指標。

channel是基於底層資料結構的引用,當我們複製一個channel或用於函式引數傳遞時,我們只是拷貝了一個channel引用。和其它的引用型別一樣,channel的零值也是nil。

3.1 channel的建立

在Go語言中,通過內建函式make可以建立一個channel,需要定義傳送到channel的值的型別。其建立格式如下:

注:a. 當capacity=0時,channel是無緩衝阻塞讀寫的;

       b. 當capacity>0時,channel有緩衝、非阻塞的,直到寫滿capacity個元素才阻塞寫入。

3.2 channel中的資料的傳送和接收

channel通過操作符<-來接收和傳送資料,傳送和接收資料的語法格式如表1所示。預設情況下,channel接收和傳送資料都是阻塞的,除非另一端已經準備好。

3.3 無緩衝的channel

3.3.1 概述

無緩衝的通道(unbuffered channel)是指在接收前沒有能力儲存任何值的通道。這種型別的通道要求同時準備好傳送goroutine和接收goroutine,才能完成傳送和接收操作。如果兩個goroutine沒有同時準備好,通道會導致先執行傳送或接收操作的goroutine阻塞等待。這種對通道進行傳送和接收的互動行為本身就是同步的,其中任意一個操作都無法離開另一個操作單獨存在。

圖1通過示意圖分析了兩個goroutine利用無緩衝通道共享一個值:

a. 第1步,兩個goroutine都到達通道兩端,但兩個都沒有開始執行傳送資料或者接收資料;

b. 第2步,左側的goroutine將它的手伸進通道,這模擬了向通道傳送資料的行為。此時,這個goroutine會在通道中被鎖住,直到交換完成;

c. 第3步,右側的goroutine將它的手放入通道,這模擬了從通道里接收資料。這個goroutine也一樣會在通道中被鎖住,直到交換完成;

d. 第4步和第5步,進行資料交換;

e. 第6步,兩個goroutine都將它們的手從通道里拿出來,這模擬了被鎖住的goroutine得到釋放。兩個goroutine現在都可以去做別的事情了。

3.3.2 無緩衝channel的建立

無緩衝channel沒有指定緩衝區容量,那麼在無緩衝的channel中,資料傳送和接收同步,其建立格式如下:

示例如下:

3.3.3 內建函式close在channel中的應用

如果傳送者知道沒有更多的值傳送到channel,那麼讓接收者也能及時知道沒有多餘的值可接收將是有用的。因為接收者可以停止不必要的接收等待,這可以通過內建的close函式來實現channel的關閉。

channel不像檔案一樣需要經常關閉,只有確實沒有任何資料傳送到channel,才關閉channel;關閉channel後,無法向channel再發送資料;對於nil channel,無論收發都會被阻塞。

3.3.4 用range接收channel中的資料

關鍵詞range結合for迴圈可用於在一個channel關閉之前,從channel中接收資料。如果要結束此操作,用close函式關閉channel。

參考資料:

https://www.cnblogs.com/tangchuanyang/p/5553434.html

http://www.flysnow.org/2017/04/11/go-in-action-go-goroutine.html

https://studygolang.com/articles/3028

本文完,獲取更多資訊,敬請關注區塊鏈工程師。