本文簡要介紹下go中協程的幾種同步方法。微信
協程相似線程,是一種更爲輕量級的調度單位,但協程仍是不一樣於線程的,線程是系統級實現的,常見的調度方法是時間片輪轉法,如每隔10ms切換一個線程執行。併發
協程則是應用軟件級實現,它和線程的原理差很少,當一個協程調度到另外一個協程時,將上一個協程的上下文信息壓入堆棧,來回切換。一個線程能夠跑不少個協程,由這個線程來調度協程的切換,若是是C/C++的話底層就能經過select/poll/epoll來作,例如微信後臺的開源libco庫。fetch
golang協程底層不是C,純go實現,golang的協程應該是目前各種有協程概念的語言中實現的最完整和成熟的,調度是基於GPM模型實現的,有興趣能夠去了解下,這裏不扯遠了,下面看看協程的同步。ui
至於爲何須要同步呢,相似線程要作同步差很少,如今的cpu都是多核,假設一核一個線程同時一塊兒訪問同一塊內存中的數據嗎,那麼可能上一ns第一個線程剛把數據從寄存器拷貝到內存,第二個線程立刻又把此數據用它修改的值給覆蓋了,這樣共享數據變會亂套。url
舉個例子 :線程
用2個協程序併發各自增一個全局變量100 0000 次code
package main import( "fmt" "time" ) var share_cnt uint64 = 0 func incrShareCnt() { for i:=0; i < 1000000; i++ { share_cnt++ } fmt.Println(share_cnt) } func main() { for i:=0; i < 2; i++ { go incrShareCnt() } time.Sleep(1000*time.Second) }
運行4次 , 能夠看到咱們雖然自增了200 0000次,但沒有一個輸出200 0000的結果.
協程
互斥鎖,能夠建立爲其餘結構體的字段;零值爲解鎖狀態。Mutex類型的鎖和線程無關,能夠由不一樣的線程加鎖和解鎖。blog
package main import( "fmt" "time" "sync" ) var share_cnt uint64 = 0 var lck sync.Mutex func incrShareCnt() { for i:=0; i < 1000000; i++ { lck.Lock() share_cnt++ lck.Unlock() } fmt.Println(share_cnt) } func main() { for i:=0; i < 2; i++ { go incrShareCnt() } time.Sleep(1000*time.Second) }
使用golang的channel, 下面一個典型的生產消費模型
package main import( "fmt" "time" "strconv" ) func main() { msg_chan := make(chan string) done := make(chan bool) i := 0 go func() { for { i++ time.Sleep(1*time.Second) msg_chan <- "on message" <- done } }() go func() { for { select { case msg := <- msg_chan : i++ fmt.Println(msg + " " + strconv.Itoa(i)) time.Sleep(2*time.Second) done <- true } } }() time.Sleep(20*time.Second) }
sync包中的WaitGroup可用等待一組協程的結束。
父協程經過Add方法來設定應等待的線程的數量。
每一個被等待的協程在結束時調用Done方法。
同時,主協程裏調用Wait方法阻塞至全部線程結束。
package main import( "sync" "net/http" ) var wg sync.WaitGroup var urls = []string{ "http://www.baidu.com/", "http://www.taobao.com/", "http://www.tianmao.com/", } func main() { for _, url := range urls { // Increment the WaitGroup counter. wg.Add(1) // Launch a goroutine to fetch the URL. go func(url string) { // Decrement the counter when the goroutine completes. defer wg.Done() // Fetch the URL. http.Get(url) }(url) } // Wait for all HTTP fetches to complete. wg.Wait() }