欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

go獲取協(xié)程(goroutine)號(hào)的實(shí)例

 更新時(shí)間:2020年12月16日 14:17:49   作者:逆月林  
這篇文章主要介紹了go獲取協(xié)程(goroutine)號(hào)的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧

我就廢話不多說了,大家還是直接看代碼吧~

func GetGID() uint64 {
    b := make([]byte, 64)
    b = b[:runtime.Stack(b, false)]
    b = bytes.TrimPrefix(b, []byte("goroutine "))
    b = b[:bytes.IndexByte(b, ' ')]
    n, _ := strconv.ParseUint(string(b), 10, 64)
    return n
}

補(bǔ)充:Go語言并發(fā)協(xié)程Goroutine和通道channel

Go語言并發(fā)協(xié)程Goroutine

1.1 Go語言競(jìng)爭(zhēng)狀態(tài)

有并發(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)。

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

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

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
  }
}

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ì)列runq,讓其他等待的 goroutine 運(yùn)行,目的是為了使資源競(jìng)爭(zhēng)的結(jié)果更明顯,下次運(yùn)行暫停的goroutine時(shí)從斷點(diǎn)處開始。

分析程序運(yùn)行過程:

g1 讀取到 count 的值為 0;

然后 g1 暫停了,切換到 g2 運(yùn)行,g2 讀取到 count 的值也為 0;

g2 暫停,切換到 g1暫停的位置繼續(xù)運(yùn)行,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)行讀寫操作。 此例子的共享資源就是count

通過go build -race生成一個(gè)可以執(zhí)行文件,然后再運(yùn)行這個(gè)可執(zhí)行文件,就可以檢測(cè)資源競(jìng)爭(zhēng)信息,看到打印出的檢測(cè)信息。如下

==================
WARNING: DATA RACE
Read at 0x000000619cbc by goroutine 8:
 main.incCount()
   D:/code/src/main.go:25 +0x80// goroutine 8 在代碼 25 行讀取共享資源value := count
Previous write at 0x000000619cbc by goroutine 7:
 main.incCount()
   D:/code/src/main.go:28 +0x9f// goroutine 7 在代碼 28行修改共享資源count=value
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//兩個(gè) goroutine 都是從 main 函數(shù)的 16、17 行通過 go 關(guān)鍵字啟動(dòng)的。
==================
4
Found 1 data race(s)

1.2 鎖住共享資源

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

1.2.1 原子函數(shù)

原子函數(shù)能夠以很底層的加鎖機(jī)制來同步訪問整型變量和指針

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è)加法操作。

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

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
    }
  }
}
--output--
Doing A Work
Doing B Work
Doing B Work
Doing A Work
Doing A Work
Doing B Work
Doing B Work
Doing A Work//前8行順序每次運(yùn)行時(shí)都不一樣
Shutdown Now
Shutting A Down
Shutting B Down//A和B都shut down后,由wg.Done()把計(jì)數(shù)器置0

上面代碼中 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)。

1.2.2 鎖

見上篇文章,上面的例子為保持同步,取消競(jìng)爭(zhēng),可照以下操作:

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()//退出當(dāng)前goroutine,調(diào)度器會(huì)再次分配這個(gè) goroutine 繼續(xù)運(yùn)行。
      value++
      counter = value
    }
    mutex.Unlock() //釋放鎖,允許其他正在等待的goroutine進(jìn)入臨界區(qū)
  }
}

1.3 通道chan

統(tǒng)統(tǒng)將通道兩端的goroutine理解為生產(chǎn)者-消費(fèi)者模式。

通道的數(shù)據(jù)接收一共有以下 4 種寫法。

阻塞接收數(shù)據(jù)

阻塞模式接收數(shù)據(jù)時(shí),將接收變量作為<-操作符的左值,格式如下:

data := <-ch

執(zhí)行該語句時(shí)將會(huì)阻塞,直到接收到數(shù)據(jù)并賦值給 data 變量。

2) 非阻塞接收數(shù)據(jù)

使用非阻塞方式從通道接收數(shù)據(jù)時(shí),語句不會(huì)發(fā)生阻塞,格式如下:

data, ok := <-ch

data:表示接收到的數(shù)據(jù)。未接收到數(shù)據(jù)時(shí),data 為通道類型的零值。

ok:表示是否接收到數(shù)據(jù)。

非阻塞的通道接收方法可能造成高的 CPU 占用,因此使用非常少。如果需要實(shí)現(xiàn)接收超時(shí)檢測(cè),可以配合 select 和計(jì)時(shí)器 channel 進(jìn)行

3) 循環(huán)接收數(shù)據(jù)

import (
  "fmt"
  "time"
)
func main() {
  // 構(gòu)建一個(gè)通道,這里有沒有緩沖都可,因?yàn)槭鞘樟司桶l(fā),無需阻塞等待
  ch := make(chan int)
  // 開啟一個(gè)并發(fā)匿名函數(shù)
  go func() {
    // 從3循環(huán)到0
    for i := 3; i >= 0; i-- {
      // 發(fā)送3到0之間的數(shù)值
      ch <- i
      // 每次發(fā)送完時(shí)等待
      time.Sleep(time.Second)
    }
  }()
  // 遍歷接收通道數(shù)據(jù)
  for data := range ch {
    // 打印通道數(shù)據(jù)
    fmt.Println(data)
    // 當(dāng)遇到數(shù)據(jù)0時(shí), 退出接收循環(huán)
    if data == 0 {
        break
    }
  }
}
--output--

1.3.1 單向通道

ch := make(chan int)
// 聲明一個(gè)只能寫入數(shù)據(jù)的通道類型, 并賦值為ch
var chSendOnly chan<- int = ch
或
ch := make(chan<- int)
//聲明一個(gè)只能讀取數(shù)據(jù)的通道類型, 并賦值為ch
var chRecvOnly <-chan int = ch
或
ch := make(<-chan int)

1.3.2 優(yōu)雅的關(guān)閉通道

1.3.3 無緩沖的通道

如果兩個(gè) goroutine 沒有同時(shí)準(zhǔn)備好,通道會(huì)導(dǎo)致先執(zhí)行發(fā)送或接收操作的 goroutine 阻塞等待。(阻塞指的是由于某種原因數(shù)據(jù)沒有到達(dá),當(dāng)前協(xié)程(線程)持續(xù)處于等待狀態(tài),直到條件滿足才解除阻塞)這種對(duì)通道進(jìn)行發(fā)送和接收的交互行為本身就是同步的。其中任意一個(gè)操作都無法離開另一個(gè)操作單獨(dú)存在。

在網(wǎng)球比賽中,兩位選手會(huì)把球在兩個(gè)人之間來回傳遞。選手總是處在以下兩種狀態(tài)之一,要么在等待接球,要么將球打向?qū)Ψ?。可以使用兩個(gè) goroutine 來模擬網(wǎng)球比賽,并使用無緩沖的通道來模擬球的來回

// 這個(gè)示例程序展示如何用無緩沖的通道來模擬
// 2 個(gè)goroutine 間的網(wǎng)球比賽
package main
import (
  "fmt"
  "math/rand"
  "sync"
  "time"
)
// wg 用來等待程序結(jié)束
var wg sync.WaitGroup
func init() {
  rand.Seed(time.Now().UnixNano())
}
// main 是所有Go 程序的入口
func main() {
  // 創(chuàng)建一個(gè)無緩沖的通道
  court := make(chan int)
  // 計(jì)數(shù)加 2,表示要等待兩個(gè)goroutine
  wg.Add(2)
  // 啟動(dòng)兩個(gè)選手
  go player("Nadal", court)
  go player("Djokovic", court)
  // 發(fā)球
  court <- 1
  // 等待游戲結(jié)束
  wg.Wait()
}
// player 模擬一個(gè)選手在打網(wǎng)球
func player(name string, court chan int) {
  // 在函數(shù)退出時(shí)調(diào)用Done 來通知main 函數(shù)工作已經(jīng)完成
  defer wg.Done()
  for {
    // 等待球被擊打過來
    ball, ok := <-court
    if !ok {
      // 如果通道被關(guān)閉,我們就贏了
      fmt.Printf("Player %s Won\n", name)
      return
    }
    // 選隨機(jī)數(shù),然后用這個(gè)數(shù)來判斷我們是否丟球
    n := rand.Intn(100)
    if n%13 == 0 {
      fmt.Printf("Player %s Missed\n", name)
      // 關(guān)閉通道,表示我們輸了
      close(court)
      return
    }
    // 顯示擊球數(shù),并將擊球數(shù)加1
    fmt.Printf("Player %s Hit %d\n", name, ball)
    ball++
    // 將球打向?qū)κ?,為啥這里是把ball發(fā)送到另一個(gè)go協(xié)程?
    //因?yàn)閏ourt無緩沖,此時(shí)另一個(gè)go協(xié)程正好在等待接收court內(nèi)的值,所以此時(shí)轉(zhuǎn)向另一個(gè)go協(xié)程代碼
    court <- ball
  }
}

1.3.4 有緩沖的通道

有緩沖的通道是一種在被接收前能存儲(chǔ)一個(gè)或者多個(gè)值的通道。這種類型的通道并不強(qiáng)制要求 goroutine 之間必須同時(shí)完成發(fā)送和接收,發(fā)送和接受的阻塞條件為只有在通道中沒有要接收的值時(shí),接收動(dòng)作才會(huì)阻塞。只有在通道沒有可用緩沖區(qū)容納被發(fā)送的值時(shí),發(fā)送動(dòng)作才會(huì)阻塞。

有緩沖的通道和無緩沖的通道之間的一個(gè)很大的不同:無緩沖的通道保證進(jìn)行發(fā)送和接收的 goroutine 會(huì)在同一時(shí)間進(jìn)行數(shù)據(jù)交換;有緩沖的通道沒有這種保證。

為什么要給通道限制緩沖區(qū)大???

通道(channel)是在兩個(gè) goroutine 間通信的橋梁。使用 goroutine 的代碼必然有一方提供數(shù)據(jù),一方消費(fèi)數(shù)據(jù)。當(dāng)提供數(shù)據(jù)一方的數(shù)據(jù)供給速度大于消費(fèi)方的數(shù)據(jù)處理速度時(shí),如果通道不限制長(zhǎng)度,那么內(nèi)存將不斷膨脹直到應(yīng)用崩潰。因此,限制通道的長(zhǎng)度有利于約束數(shù)據(jù)提供方的供給速度,供給數(shù)據(jù)量必須在消費(fèi)方處理量+通道長(zhǎng)度的范圍內(nèi),才能正常地處理數(shù)據(jù)。

1.3.5 channel超時(shí)機(jī)制

select 機(jī)制不是專門為超時(shí)而設(shè)計(jì)的,卻能很方便的解決超時(shí)問題,因?yàn)?select 的特點(diǎn)是只要其中有一個(gè) case 已經(jīng)完成,程序就會(huì)繼續(xù)往下執(zhí)行,而不會(huì)考慮其他 case 的情況。

基本語句為:

每個(gè) case 語句里必須是一個(gè) IO 操作,

select {
  case <-chan1:
  // 如果chan1成功讀到數(shù)據(jù),則進(jìn)行該case處理語句
  case chan2 <- 1:
  // 如果成功向chan2寫入數(shù)據(jù),則進(jìn)行該case處理語句
  default:
  // 如果上面都沒有成功,則進(jìn)入default處理流程
}

例子,注意之所以輸出5個(gè)num,是因?yàn)閟elect里的time.After在這里的意思是ch通道無值可以接收的時(shí)候的3s后才print超時(shí),即最多ch通道最多阻塞等待3s

func main() {
  ch := make(chan int)
  quit := make(chan bool)
  //新開一個(gè)協(xié)程
  go func() {
    for {
      select {
      case num := <-ch:
        fmt.Println("num = ", num)
      case <-time.After(3 * time.Second):
        fmt.Println("超時(shí)")
        quit <- true
      }
    }
  }() //別忘了()
  for i := 0; i < 5; i++ {
    ch <- i
    time.Sleep(time.Second)//主協(xié)程進(jìn)入休眠狀態(tài),等待上面的go協(xié)程運(yùn)行并進(jìn)入阻塞等待狀態(tài),就這樣來回運(yùn)行,并通過chan通信
  }
  <-quit
  fmt.Println("程序結(jié)束")
}
--output--
num = 0
num = 1
num = 2
num = 3
num = 4
超時(shí)
程序結(jié)束

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。

相關(guān)文章

  • Golang?動(dòng)態(tài)腳本調(diào)研詳解

    Golang?動(dòng)態(tài)腳本調(diào)研詳解

    這篇文章主要為大家介紹了Golang?動(dòng)態(tài)腳本調(diào)研詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-09-09
  • Go語言中字符串與字節(jié)切片轉(zhuǎn)換的詳細(xì)過程

    Go語言中字符串與字節(jié)切片轉(zhuǎn)換的詳細(xì)過程

    在Go語中,字符串(string)和字節(jié)切片([]byte)是處理文本和二進(jìn)制數(shù)據(jù)的重要類型,理解它們的轉(zhuǎn)換機(jī)制及底層表示形式,是編寫高效代碼的關(guān)鍵,本文將深入探討字符串與字節(jié)切片的轉(zhuǎn)換過程,并通過示例和流程圖幫助讀者掌握其核心原理,需要的朋友可以參考下
    2025-05-05
  • go第三方庫sqlx操作MySQL及ORM原理

    go第三方庫sqlx操作MySQL及ORM原理

    這篇文章主要為大家介紹了go第三方庫sqlx操作MySQL及ORM實(shí)現(xiàn)原理,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-05-05
  • 深入了解Go項(xiàng)目標(biāo)準(zhǔn)目錄布局

    深入了解Go項(xiàng)目標(biāo)準(zhǔn)目錄布局

    本文主要介紹了Go項(xiàng)目標(biāo)準(zhǔn)目錄布局,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • 四種Golang實(shí)現(xiàn)middleware框架的方式小結(jié)

    四種Golang實(shí)現(xiàn)middleware框架的方式小結(jié)

    middleware是一般框架里面常用的形式,比如web框架、rpc框架等,本文為大家詳細(xì)介紹了四種實(shí)現(xiàn)middleawre的方式,感興趣的可以了解一下
    2024-03-03
  • Golang filepath包常用函數(shù)詳解

    Golang filepath包常用函數(shù)詳解

    本文介紹與文件路徑相關(guān)包,該工具包位于path/filepath中,該包試圖與目標(biāo)操作系統(tǒng)定義的文件路徑兼容。本文介紹一些常用函數(shù),如獲取文件絕對(duì)路徑,獲取文件名或目錄名、遍歷文件、分割文件路徑、文件名模式匹配等函數(shù),并給具體示例進(jìn)行說明
    2023-02-02
  • Go?通過?Map/Filter/ForEach?等流式?API?高效處理數(shù)據(jù)的思路詳解

    Go?通過?Map/Filter/ForEach?等流式?API?高效處理數(shù)據(jù)的思路詳解

    Stream?的實(shí)現(xiàn)思想就是將數(shù)據(jù)處理流程抽象成了一個(gè)數(shù)據(jù)流,每次加工后返回一個(gè)新的流供使用。這篇文章主要介紹了Go?通過?Map/Filter/ForEach?等流式?API?高效處理數(shù)據(jù),需要的朋友可以參考下
    2022-01-01
  • Go語言之ORM(對(duì)象關(guān)系映射)庫詳解

    Go語言之ORM(對(duì)象關(guān)系映射)庫詳解

    GORM是Go語言流行ORM庫,支持多數(shù)據(jù)庫與結(jié)構(gòu)體映射表,具備鏈?zhǔn)紸PI、自動(dòng)遷移、關(guān)聯(lián)操作等功能,原倉庫已歸檔,推薦使用GORMv2,性能優(yōu)化且API更清晰,適合快速開發(fā),不適用于性能極致需求
    2025-07-07
  • 淺談golang 的高效編碼細(xì)節(jié)

    淺談golang 的高效編碼細(xì)節(jié)

    本文主要介紹了golang 的高效編碼細(xì)節(jié),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-10-10
  • Go語言的常量、枚舉、作用域示例詳解

    Go語言的常量、枚舉、作用域示例詳解

    這篇文章主要介紹了Go語言的常量、枚舉、作用域,接下來,我們將詳細(xì)了解 Go 的變量作用域規(guī)則以及這些規(guī)則如何影響代碼編寫,需要的朋友可以參考下
    2024-07-07

最新評(píng)論