Go語言併發程式設計(二)

2021-07-04 23:32:29 字數 4125 閱讀 5122

使用非常簡單,在函式前增加乙個go

例:go f(a,b)//開啟後,不等待其結束,主線程繼續執行。

ps:要注意的是乙個goroutine開啟後,若不等其執行,main(主goroutine)中將繼續執行下一步,那麼主線程一結束,goroutine中的程式就不會執行了。如何解決?**如下:

func saysomething(str string) 

}func main()

這裡為什麼要sleep? 是為了等go saysomething(「hello」)處理完成。

好了,這裡就出來了乙個需求:如果要人為設定乙個休眠的時間,非常地不方便,需要使乙個goroutine結束後自動向主線程傳輸資料,告訴主線程這個goroutine已經結束了。這裡就引進了channel的概念。

簡單來說就是,主線程告訴大家你開goroutine可以,但是我在我的主線程開了乙個通道,你做完了你要做的事情之後,往通道裡面塞個東西告訴我你已經完成了,我再結束主線程。

1、和map一樣,通道是引用型別,用make 分配記憶體。如果呼叫make時提供乙個可選的整數引數,則該通道就會被分配相應大小的緩衝區。緩衝區大小預設為0,對應於無緩衝通道或者同步通道。

ci := make(chan

int) // 無緩衝整數通道

cs := make(chan *os.file,100) // 緩衝的檔案指標通道

2、通道可用來讓正在執行的goroutine等待排序完成。確保(goroutine)相互都處於已知狀態。

-往channel中插入資料的操作

c

<- 1

-從channel中輸出資料

<- c
**示例:

c := make(chan

int) // allocate a channel.

// 在goroutine中啟動排序,當排序完成時,通道上發出訊號

gofunc() ()

dosomethingforawhile()

<-c // 等待排序完成,丟棄被傳送的值。

收信者(receivers)在收到資料前會一直被阻滯。如果通道是非緩衝的,則發信者(sender)在收信者接收到資料前也一直被阻滯。如果通道有緩衝區,發信者只有在資料被填入緩衝區前才被阻滯;如果緩衝區是滿的,意味著傳送者要等到某個收信者取走乙個值。

緩衝的通道可以象訊號燈一樣使用,比如用來限制吞吐量。在下面的例子中,進入的請求被傳遞給handle,handle傳送乙個值到通道,接著處理請求,最後從通道接收乙個值。通道緩衝區的大小限制了併發呼叫process的數目。

var sem = make(chan

int, maxoutstanding)

func handle(r *request)

func serve(queue chan *request)

}

通過啟動固定數目的handle goroutines也可以實現同樣的功能,這些goroutines都從請求通道中讀取請求。goroutines的數目限制了併發呼叫process的數目。serve函式也從乙個通道中接收退出訊號;在啟動goroutines後,它處於阻滯狀態,直到接收到退出訊號:

func handle(queue chan *request) 

}func serve(clientrequests chan *clientrequests, quit chan

bool)

<-quit // 等待退出訊號

}

go最重要的特性之一就是: 通道, 通道可以像其它型別的數值一樣被分配記憶體並傳遞。此特性常用於實現安全且並行的去復用(demultiplexing)。

前面的例子中,handle是乙個理想化的處理請求的函式,但是我們沒有定義它所能處理的請求的具體型別。如果該型別包括了乙個通道,每個客戶端就可以提供自己方式進行應答

type request struct
客戶端提供乙個函式、該函式的引數以及乙個請求物件用來接收應答的通道

func sum(a int) (s int) 

return

}request := &request, sum, make(chan

int)}

// 傳送請求

clientrequests <- request

// 等待響應.

fmt.printf("answer: %d\n", <-request.resultchan)

在伺服器端,處理請求的函式是

func handle(queue chan *request) 

}

顯然要使這個例子更為實際還有很多任務作要做,但這是針對速度限制、並行、非阻滯rpc系統的框架,而且其中也看不到互斥(mutex)的使用。

並行思想的乙個應用是利用多核cpu進行平行計算。如果計算過程可以被分為多個片段,則它可以通過這樣一種方式被並行化:在每個片段完成後通過通道傳送訊號。

此處上篇文章已經介紹過了,詳見go語言併發程式設計(一)

設定乙個變數作為同步工具。這是防止主goroutine過早的被執行結束的有效手段之一。對這個變數的宣告和初始化的**如下:

var waitgroup sync.waitgroup // 用於等待一組操作執行完畢的同步工具。

waitgroup.add(3) // 該組操作的數量是3。

numberchan1 := make(chan

int64,3) // 數字通道1。

numberchan2 := make(chan

int64,3) // 數字通道2。

numberchan3 := make(chan

int64,3) // 數字通道3

識別符號sync.waitgroup代表了乙個型別。該型別的宣告存在於**包sync中,型別名為waitgroup。另外,上面的第二條語句進行了乙個「加3」的操作,意味著我們將要後面啟用三個goroutine,或者說要併發的執行三個go函式。

先來看第乙個go函式:數字過濾函式,過濾掉不能被2整除的數字。

go

func() else

} close(numberchan2) // 關閉數字通道2。

waitgroup.done() // 表示此操作完成。進行相應的「減1」

}()

數字過濾函式2**與上述類似,過濾掉不能被5整除的數字。如下:

go

func() else

} close(numberchan3) // 關閉數字通道3。

waitgroup.done() // 表示此操作完成。進行相應的「減1」

}()

如此一來,數字過濾函式1和2就經由數字通道2串聯起來了。請注意,不要忘記在數字過濾函式2中的for語句後面新增對數字通道numberchan3的關閉操作,以及呼叫waitgroup變數的done方法。

go

func()

waitgroup.done() // 表示此操作完成。並「減1」。

}()

然後啟用這一過濾數字的流程。具體的啟用方法是,向數字通道numberchan1傳送數字。在上述**後加入**如下:

for i := 0; i < 100; i++ 

close(numberchan1)// 數字傳送完畢,關閉數字通道1。對通道的關閉並不會影響到對已存於其中的數字的接收操作。

為了能夠讓這個流程能夠被完整的執行,我們還需要在最後加入這樣一條語句:

waitgroup.wait() // 等待前面那組操作(共3個)的完成。
對waitgroup的wait方法的呼叫會一直被阻塞,直到前面三個go函式中的三個waitgroup.done()語句(即那三個「減1操作」)都被執行完畢。也就是當waitgroup裡的數量由3減到0時,才能讓對waitgroup.wait()語句的執行從阻塞中恢復並完成。

go語言併發程式設計

協程 coroutine 本質上是一種使用者態執行緒,不需要作業系統來進行搶占式排程,且在真正的實現中寄存於執行緒中,系統開銷極小。package main import fmt func count ch chan int,i int func main for ch range chs chan...

Go語言 併發程式設計goroutine

在go語言中併發是通過goroutine實現。goroutine類似於執行緒,屬於使用者態執行緒。go語言也可以通過channel 管道 與多個goroutine進行通訊。goroutine類似於執行緒,在go語言中底層分配了乙個執行緒池,因此不需要我們對其進行管理,由go執行時的routine進行...

Go語言併發程式設計 讀寫鎖

通過對互斥鎖的學習,我們已經了解了鎖的概念及用途。主要用於處理併發中的臨界資源問題。rwmutex是基於mutex實現的,唯讀鎖的實現使用類似引用計數器的功能。rwmutext是讀 寫互斥鎖。鎖可以由任意數量的讀取器或單個編寫器持有。rwmutex的零值是未鎖定的mutex。當有乙個goroutin...