日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
創(chuàng)新互聯(lián)GO教程:Go語(yǔ)言競(jìng)爭(zhēng)狀態(tài)簡(jiǎn)述

有并發(fā),就有資源競(jìng)爭(zhēng),如果兩個(gè)或者多個(gè) goroutine 在沒有相互同步的情況下,訪問某個(gè)共享的資源,比如同時(shí)對(duì)該資源進(jìn)行讀寫時(shí),就會(huì)處于相互競(jìng)爭(zhēng)的狀態(tài),這就是并發(fā)中的資源競(jìng)爭(zhēng)。

創(chuàng)新互聯(lián)公司專業(yè)為企業(yè)提供容城網(wǎng)站建設(shè)、容城做網(wǎng)站、容城網(wǎng)站設(shè)計(jì)、容城網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁(yè)設(shè)計(jì)與制作、容城企業(yè)網(wǎng)站模板建站服務(wù),十余年容城做網(wǎng)站經(jīng)驗(yàn),不只是建網(wǎng)站,更提供有價(jià)值的思路和整體網(wǎng)絡(luò)服務(wù)。

并發(fā)本身并不復(fù)雜,但是因?yàn)橛辛速Y源競(jìng)爭(zhēng)的問題,就使得我們開發(fā)出好的并發(fā)程序變得復(fù)雜起來,因?yàn)闀?huì)引起很多莫名其妙的問題。

下面的代碼中就會(huì)出現(xiàn)競(jìng)爭(zhēng)狀態(tài):

package main

import (
    "fmt"
    "runtime"
    "sync"
)

var (
    count int32
    wg    sync.WaitGroup
)

func main() {
    wg.Add(2)
    go incCount()
    go incCount()
    wg.Wait()
    fmt.Println(count)
}

func incCount() {
    defer wg.Done()
    for i := 0; i < 2; i++ {
        value := count
        runtime.Gosched()
        value++
        count = value
    }
}

這是一個(gè)資源競(jìng)爭(zhēng)的例子,大家可以將程序多運(yùn)行幾次,會(huì)發(fā)現(xiàn)結(jié)果可能是 2,也可以是 3,還可能是 4。這是因?yàn)?count 變量沒有任何同步保護(hù),所以兩個(gè) goroutine 都會(huì)對(duì)其進(jìn)行讀寫,會(huì)導(dǎo)致對(duì)已經(jīng)計(jì)算好的結(jié)果被覆蓋,以至于產(chǎn)生錯(cuò)誤結(jié)果。

代碼中的 runtime.Gosched() 是讓當(dāng)前 goroutine 暫停的意思,退回執(zhí)行隊(duì)列,讓其他等待的 goroutine 運(yùn)行,目的是為了使資源競(jìng)爭(zhēng)的結(jié)果更明顯。

下面我們來分析一下程序的運(yùn)行過程,將兩個(gè) goroutine 分別假設(shè)為 g1 和 g2:

  • g1 讀取到 count 的值為 0;
  • 然后 g1 暫停了,切換到 g2 運(yùn)行,g2 讀取到 count 的值也為 0;
  • g2 暫停,切換到 g1,g1 對(duì) count+1,count 的值變?yōu)?1;
  • g1 暫停,切換到 g2,g2 剛剛已經(jīng)獲取到值 0,對(duì)其 +1,最后賦值給 count,其結(jié)果還是 1;
  • 可以看出 g1 對(duì) count+1 的結(jié)果被 g2 給覆蓋了,兩個(gè) goroutine 都 +1 而結(jié)果還是 1。

通過上面的分析可以看出,之所以出現(xiàn)上面的問題,是因?yàn)閮蓚€(gè) goroutine 相互覆蓋結(jié)果。

所以我們對(duì)于同一個(gè)資源的讀寫必須是原子化的,也就是說,同一時(shí)間只能允許有一個(gè) goroutine 對(duì)共享資源進(jìn)行讀寫操作。

共享資源競(jìng)爭(zhēng)的問題,非常復(fù)雜,并且難以察覺,好在 Go 為我們提供了一個(gè)工具幫助我們檢查,這個(gè)就是
go build -race 命令。在項(xiàng)目目錄下執(zhí)行這個(gè)命令,生成一個(gè)可以執(zhí)行文件,然后再運(yùn)行這個(gè)可執(zhí)行文件,就可以看到打印出的檢測(cè)信息。


go build命令中多加了一個(gè)
-race 標(biāo)志,這樣生成的可執(zhí)行程序就自帶了檢測(cè)資源競(jìng)爭(zhēng)的功能,運(yùn)行生成的可執(zhí)行文件,效果如下所示:

==================
WARNING: DATA RACE
Read at 0x000000619cbc by goroutine 8:
  main.incCount()
      D:/code/src/main.go:25 +0x80

Previous write at 0x000000619cbc by goroutine 7:
  main.incCount()
      D:/code/src/main.go:28 +0x9f

Goroutine 8 (running) created at:
  main.main()
      D:/code/src/main.go:17 +0x7e

Goroutine 7 (finished) created at:
  main.main()
      D:/code/src/main.go:16 +0x66
==================
4
Found 1 data race(s)

通過運(yùn)行結(jié)果可以看出 goroutine 8 在代碼 25 行讀取共享資源
value := count,而這時(shí) goroutine 7 在代碼 28 行修改共享資源
count = value,而這兩個(gè) goroutine 都是從 main 函數(shù)的 16、17 行通過 go 關(guān)鍵字啟動(dòng)的。

鎖住共享資源

Go語(yǔ)言提供了傳統(tǒng)的同步 goroutine 的機(jī)制,就是對(duì)共享資源加鎖。atomic 和 sync 包里的一些函數(shù)就可以對(duì)共享的資源進(jìn)行加鎖操作。

原子函數(shù)

原子函數(shù)能夠以很底層的加鎖機(jī)制來同步訪問整型變量和指針,示例代碼如下所示:

package main

import (
    "fmt"
    "runtime"
    "sync"
    "sync/atomic"
)

var (
    counter int64
    wg      sync.WaitGroup
)

func main() {
    wg.Add(2)
    go incCounter(1)
    go incCounter(2)

    wg.Wait() //等待goroutine結(jié)束
    fmt.Println(counter)
}

func incCounter(id int) {
    defer wg.Done()
    for count := 0; count < 2; count++ {
        atomic.AddInt64(&counter, 1) //安全的對(duì)counter加1

        runtime.Gosched()
    }
}

上述代碼中使用了 atmoic 包的 AddInt64 函數(shù),這個(gè)函數(shù)會(huì)同步整型值的加法,方法是強(qiáng)制同一時(shí)刻只能有一個(gè) gorountie 運(yùn)行并完成這個(gè)加法操作。當(dāng) goroutine 試圖去調(diào)用任何原子函數(shù)時(shí),這些 goroutine 都會(huì)自動(dòng)根據(jù)所引用的變量做同步處理。

另外兩個(gè)有用的原子函數(shù)是 LoadInt64 和 StoreInt64。這兩個(gè)函數(shù)提供了一種安全地讀和寫一個(gè)整型值的方式。下面是代碼就使用了 LoadInt64 和 StoreInt64 函數(shù)來創(chuàng)建一個(gè)同步標(biāo)志,這個(gè)標(biāo)志可以向程序里多個(gè) goroutine 通知某個(gè)特殊狀態(tài)。

package main

import (
    "fmt"
    "sync"
    "sync/atomic"
    "time"
)

var (
    shutdown int64
    wg       sync.WaitGroup
)

func main() {
    wg.Add(2)

    go doWork("A")
    go doWork("B")

    time.Sleep(1 * time.Second)
    fmt.Println("Shutdown Now")
    atomic.StoreInt64(&shutdown, 1)
    wg.Wait()
}

func doWork(name string) {
    defer wg.Done()

    for {
        fmt.Printf("Doing %s Work\n", name)
        time.Sleep(250 * time.Millisecond)

        if atomic.LoadInt64(&shutdown) == 1 {
            fmt.Printf("Shutting %s Down\n", name)
            break
        }
    }
}

上面代碼中 main 函數(shù)使用 StoreInt64 函數(shù)來安全地修改 shutdown 變量的值。如果哪個(gè) doWork goroutine 試圖在 main 函數(shù)調(diào)用 StoreInt64 的同時(shí)調(diào)用 LoadInt64 函數(shù),那么原子函數(shù)會(huì)將這些調(diào)用互相同步,保證這些操作都是安全的,不會(huì)進(jìn)入競(jìng)爭(zhēng)狀態(tài)。

互斥鎖

另一種同步訪問共享資源的方式是使用互斥鎖,互斥鎖這個(gè)名字來自互斥的概念?;コ怄i用于在代碼上創(chuàng)建一個(gè)臨界區(qū),保證同一時(shí)間只有一個(gè) goroutine 可以執(zhí)行這個(gè)臨界代碼。

示例代碼如下所示:

package main

import (
    "fmt"
    "runtime"
    "sync"
)

var (
    counter int64
    wg      sync.WaitGroup
    mutex   sync.Mutex
)

func main() {
    wg.Add(2)

    go incCounter(1)
    go incCounter(2)

    wg.Wait()
    fmt.Println(counter)
}

func incCounter(id int) {
    defer wg.Done()

    for count := 0; count < 2; count++ {
        //同一時(shí)刻只允許一個(gè)goroutine進(jìn)入這個(gè)臨界區(qū)
        mutex.Lock()
        {
            value := counter
            runtime.Gosched()
            value++
            counter = value
        }
        mutex.Unlock() //釋放鎖,允許其他正在等待的goroutine進(jìn)入臨界區(qū)
    }
}

同一時(shí)刻只有一個(gè) goroutine 可以進(jìn)入臨界區(qū)。之后直到調(diào)用 Unlock 函數(shù)之后,其他 goroutine 才能進(jìn)去臨界區(qū)。當(dāng)調(diào)用 runtime.Gosched 函數(shù)強(qiáng)制將當(dāng)前 goroutine 退出當(dāng)前線程后,調(diào)度器會(huì)再次分配這個(gè) goroutine 繼續(xù)運(yùn)行。


文章標(biāo)題:創(chuàng)新互聯(lián)GO教程:Go語(yǔ)言競(jìng)爭(zhēng)狀態(tài)簡(jiǎn)述
URL網(wǎng)址:http://www.5511xx.com/article/cdejoei.html