這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
What you are wasting today is tomorrow for those who died yesterday; what you hate now is the future you can not go back.
你所浪費的今天是昨天死去的人奢望的明天; 你所厭惡的現在是未來的你回不去的曾經。
Buffered channel
之前我們說的channel都是不帶緩衝的,無論發送和接收都會導致阻塞。
緩衝Channel的特點是:只有當發送至緩衝區存滿後導致阻塞, 接受也是如此。
建立方式: ch:= make(chan Type , capacity)
capacity 容量, 當capacity = 0 時, 為無緩衝channel,通常省略而已。
package mainimport ("fmt")func main() {ch := make(chan string, 2)ch <- "naveen"ch <- "paul"fmt.Println(<- ch)fmt.Println(<- ch) // 注釋此行,會不會deadlock???}
下這個例子請認真思考,有助於理解buffered channel:
package mainimport ( "fmt" "time")func write(ch chan int) { for i := 0; i < 5; i++ { ch <- i fmt.Println("successfully wrote", i, "to ch") } close(ch)}func main() { ch := make(chan int, 2) go write(ch) time.Sleep(2 * time.Second) for v := range ch { fmt.Println("read value", v,"from ch") time.Sleep(2 * time.Second) }}
解釋:
當main程建立一個有容量為2的channel,然後在goroutine中迴圈寫入, 在寫入兩次後, goroutine阻塞, main程同時也進入了sleep中,當range開始接收後,goroutine發現又可以繼續寫入。
輸出:
successfully wrote 0 to chsuccessfully wrote 1 to chread value 0 from chsuccessfully wrote 2 to chread value 1 from chsuccessfully wrote 3 to chread value 2 from chsuccessfully wrote 4 to chread value 3 from chread value 4 from ch
一開始寫入兩次,是因為channel容量為2, 不需要讀取就可寫入。
最後連續兩次讀,是因為當range讀取一次後, goroutine立刻寫入一次,所以channel中始終保持2個資料。
概念: 長度與容量
容量是指channel最大的儲存長度。 長度是指當前channel中正在排隊的資料長度。
代碼說明:
package mainimport ( "fmt")func main() { ch := make(chan string, 3) ch <- "資料1"ch <- "資料2" //容量為3, 但是其中資料只有2個 fmt.Println("capacity is", cap(ch)) //資料長度為2 fmt.Println("length is", len(ch)) //讀取一次 fmt.Println("read value", <-ch) //資料長度為1, 但是容量還是3 fmt.Println("new length is", len(ch)) }
輸出:
capacity is 3length is 2read value 資料1new length is 1
WaitGroup
工作池的實現離不開WaitGroup, 下面講一下關於WariGroup。
如果一個main程中有三個goroutine, 要想獲得這三個goroutine的輸出,那麼 需要使用WaitGroup阻塞main程,等待所有goroutine結束。
package mainimport ("fmt""sync""time")func ProcessEcho( i int , w *sync.WaitGroup){fmt.Println("協程", i , "開始")time.Sleep(1*time.Second)fmt.Println("協程", i , "結束")w.Done()}func main(){var w sync.WaitGroupMax := 10for i:= 0; i<Max ;i++ {w.Add(1)go ProcessEcho(i, &w)}w.Wait()fmt.Println("main執行完成並退出。")}
解釋:
main程啟動10個協程, 每天啟動都高速WaitGroup來添加一個監聽,每個goroutine結束都標記一次結束。 main程中等待所有標記完成,結束阻塞。
注意點:
1. 為什麼go ProcessEcho中使用的是w的指標?!
2. goroutine的輸出是沒有規律的。
細看工作池的實現吧:
package mainimport ("fmt""sync""time")//任務結構type Job struct {id intrandomno int}//接受資料結構type Result struct {job Jobsumofdigits int}var jobs = make(chan Job, 10)var results = make(chan Result, 10)func digits(number int) int {time.Sleep(2 * time.Second)return number}func worker(i int , wg *sync.WaitGroup) {for job := range jobs {output := Result{job, digits(i)}results <- output}wg.Done()}func createWorkerPool(noOfWorkers int) {var wg sync.WaitGroupfor i := 0; i < noOfWorkers; i++ {wg.Add(1)go worker(i,&wg)}wg.Wait()close(results)}func allocate(noOfJobs int) {for i := 0; i < noOfJobs; i++ {randomno := ijob := Job{i, randomno}jobs <- job}close(jobs)}func result(done chan bool) {for result := range results {fmt.Printf("Job id %d, input random no %d , sum of digits %d\n", result.job.id, result.job.randomno, result.sumofdigits)}done <- true}func main() {startTime := time.Now()noOfJobs := 12 // 任務數go allocate(noOfJobs)done := make(chan bool)go result(done)noOfWorkers := 3 // 執行者createWorkerPool(noOfWorkers)<-doneendTime := time.Now()diff := endTime.Sub(startTime)fmt.Println("total time taken ", diff.Seconds(), "seconds")}