golang協程同步的幾種方法

golang協程同步的幾種方法


本文簡要介紹下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的結果.
Alt text協程

協程的幾種同步方法

Mutex

互斥鎖,能夠建立爲其餘結構體的字段;零值爲解鎖狀態。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)

}

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

}
相關文章
相關標籤/搜索