golang協程同步的幾種方法


golang協程同步的幾種方法


本文簡要介紹下go中協程的幾種同步方法。

協程概念簡要理解

協程類似線程,是一種更為輕量級的調度單位,但協程還是不同於線程的,線程是系統級實現的,常見的調度方法是時間片輪轉法,如每隔10ms切換一個線程執行。

協程則是應用軟件級實現,它和線程的原理差不多,當一個協程調度到另一個協程時,將上一個協程的上下文信息壓入堆棧,來回切換。一個線程可以跑很多個協程,由這個線程來調度協程的切換,如果是C/C++的話底層就能通過select/poll/epoll來做,例如微信后台的開源libco庫。

golang協程底層不是C,純go實現,golang的協程應該是目前各類有協程概念的語言中實現的最完整和成熟的,調度是基於GPM模型實現的,有興趣可以去了解下,這里不扯遠了,下面看看協程的同步。

為什么要做同步

至於為什么需要同步呢,類似線程要做同步差不多,現在的cpu都是多核,假設一核一個線程同時一起訪問同一塊內存中的數據嗎,那么可能上一ns第一個線程剛把數據從寄存器拷貝到內存,第二個線程馬上又把此數據用它修改的值給覆蓋了,這樣共享數據變會亂套。

舉個例子 :

用2個協程序並發各自增一個全局變量100 0000 次

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的結果.
Alt text

協程的幾種同步方法

Mutex

互斥鎖,可以創建為其他結構體的字段;零值為解鎖狀態。Mutex類型的鎖和線程無關,可以由不同的線程加鎖和解鎖。

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)

}

Alt text

channel

使用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)
}

Alt text

WaitGroup

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()

}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM