并發(fā)模式之外延
協(xié)程相較于線程,可以大量創(chuàng)建。打開這扇門,我們拓展出新的用法,可以做生成器,可以讓函數(shù)返回“服務”,可以讓循環(huán)并發(fā)執(zhí)行,還能共享變量。但是出現(xiàn)新 的用法的同時,也帶來了新的棘手問題,協(xié)程也會泄漏,不恰當?shù)氖褂脮绊懶阅。下面會逐一介紹各種用法和問題。演示的代碼用GO語言寫成,因為其簡潔明 了,而且支持全部功能。
生成器
有的時候,我們需要有一個函數(shù)能不斷生成數(shù)據(jù)。比方說這個函數(shù)可以讀文件,讀網(wǎng)絡,生成自增長序列,生成隨機數(shù)。這些行為的特點就是,函數(shù)的已知一些變量,如文件路徑。然后不斷調用,返回新的數(shù)據(jù)。
下面生成隨機數(shù)為例,以讓我們做一個會并發(fā)執(zhí)行的隨機數(shù)生成器。
非并發(fā)的做法是這樣的:
// 函數(shù)rand_generator_1 ,返回 int
funcrand_generator_1() int {
return rand.Int()
}
上面是一個函數(shù),返回一個int。假如rand.Int()這個函數(shù)調用需要很長時間等待,那該函數(shù)的調用者也會因此而掛起。所以我們可以創(chuàng)建一個協(xié)程,專門執(zhí)行rand.Int()。
// 函數(shù)rand_generator_2,返回通道(Channel)
funcrand_generator_2() chan int {
// 創(chuàng)建通道
out := make(chan int)
// 創(chuàng)建協(xié)程
go func() {
for {
//向通道內寫入數(shù)據(jù),如果無人讀取會等待
out <- rand.Int()
}
}()
return out
}
funcmain() {
// 生成隨機數(shù)作為一個服務
rand_service_handler :=rand_generator_2()
// 從服務中讀取隨機數(shù)并打印
fmt.Printf("%d\n",<-rand_service_handler)
}
上面的這段函數(shù)就可以并發(fā)執(zhí)行了rand.Int()。有一點值得注意到函數(shù)的返回可以理解為一個“服務”。但我們需要獲取隨機數(shù)據(jù)時候,可以隨時向這個 服務取用,他已經(jīng)為我們準備好了相應的數(shù)據(jù),無需等待,隨要隨到。如果我們調用這個服務不是很頻繁,一個協(xié)程足夠滿足我們的需求了。但如果我們需要大量訪 問,怎么辦?我們可以用下面介紹的多路復用技術,啟動若干生成器,再將其整合成一個大的服務。
調用生成器,可以返回一個“服務”。可以用在持續(xù)獲取數(shù)據(jù)的場合。用途很廣泛,讀取數(shù)據(jù),生成ID,甚至定時器。這是一種非常簡潔的思路,將程序并發(fā)化。