這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
某些資訊從舊的資源集區中取出來,經過一些加工處理,再放入新的資源集區中,這個過程如果按傳統的方式就是採用完全串列的方式效率會很低,粒度太粗了,具體的粒度可以細化以每次所取的單位資源為粒度。
有一個資源集區儲存這person的資訊,將每個person從中取出來,之後進行一些處理,再存到新的資源集區中,這裡用oldarray以及newarray來類比舊的和新的資源集區:
代碼如下:
type Person struct { name string age int addr string}var oldpersonarray = [5]Person{}var newpersonarray = [5]Person{}type PersonHandler interface { Batch(origs <-chan Person) <-chan Person Handle(orig *Person)}//struct 實現了personhandler 介面type PersonHandlerImpl struct{}//從origs接收資訊 處理之後再返回給新的channelfunc (handler PersonHandlerImpl) Batch(origs <-chan Person) <-chan Person { dests := make(chan Person, 100) go func() { for { p, ok := <-origs if !ok { close(dests) break } handler.Handle(&p) log.Printf("old value : %v\n", p) //time.Sleep(time.Second) dests <- p } }() return dests}//這裡要使用引用傳遞func (handler PersonHandlerImpl) Handle(orig *Person) { orig.addr = "new address"}func getPersonHandler() PersonHandler { return &PersonHandlerImpl{}}//print the oldpersonarray into the chan<-Personfunc fetchPerson(origs chan<- Person) { for _, v := range oldpersonarray { fmt.Printf("get the value : %v \n", v) time.Sleep(time.Second) origs <- v } close(origs)}//fetch the value from the channel and store it into the newpersonarrayfunc savePerson(dest <-chan Person) <-chan int { intChann := make(chan int) go func() { index := 0 for { p, ok := <-dest if !ok { break } time.Sleep(time.Second) log.Printf("new value transfer %v \n", p) newpersonarray[index] = p index++ } intChann <- 1 }() return intChann}func init() { //使用range的話是值傳遞 這裡要給oldpersonarray賦值進來 tmplen := len(oldpersonarray) for i := 0; i < tmplen; i++ { oldpersonarray[i].addr = "old address" oldpersonarray[i].age = i oldpersonarray[i].name = strconv.Itoa(i) } log.Printf("first print init value : %v\n", oldpersonarray)}func main() { handeler := getPersonHandler() origs := make(chan Person, 100) dests := handeler.Batch(origs) go func() { fetchPerson(origs) }() // 不加go func的話 要等這句執行完 才能執行下一句 // 則orgis資訊都輸出 完全關閉掉 這個時候 從dest接收資訊的語句才開始執行 // 所以不會動態輸出 這句加上go func的話 就會沒隔 1s 動態輸出 // 如果將fetchPerson 再往前面放一句 則old value也不會動態輸出 //fetchPerson(origs) sign := savePerson(dests) log.Println(<-sign) log.Printf("last print new value : %v \n", newpersonarray)}
- 首先聲明一個 PersonHandler 的介面,之後聲明一個struct PersonHandlerImpl 將介面中的兩個方法都實現了,init函數用於進行oldarray的初始化工作。注意為了減少出錯,內部的函數在方聲明的時候都是單向的channel。
- 1,2 fetchperson從oldarray中區資料,並把資料存到origs channel中,注意最後取完資料到通道之後,要由發送方將channel關閉,否則可能造成deadlock。注意在main函數中,如果fech操作沒有放到一個goroutine中來執行,就仍然是串列的,相當於是把資料都放入到channel中,另一端才開始取,沒發揮出並發的優勢。
- 3,4 Batch函數將person資訊從origs中取出來,進行處理後,同時傳到dests中,最後將dests返回,注意這裡不是全部傳入之後才將dests返回,而是新啟動一個goroutine執行傳入操作,同時將dests返回,注意要主動關閉channel。
- 5 savePerson操作接收一個<-chann 之後從中接受person資訊,將值寫入到新的資源集區中,最後全部寫入結束之後,傳一個sign channel給主進程,結束。
- 總結,在需要動態輸出資訊的時候,goroutine往往是和channel結合在一起使用。最常見的用法是,一個goroutine負責向channel中寫入資料,之後將channel返回,由其他進程取出資訊。比如之前寫過的一些websocket從前台接受資訊,幕後處理資訊之後再動態返回給前台打出結果的模型,就和這個差不多,總之具體的非同步執行流程要理清楚,都有哪些channel,負責傳遞的資訊分別是什麼。