使用非常簡單,在函式前增加乙個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...