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

Golang并發(fā)繞不開的重要組件之Goroutine詳解

 更新時(shí)間:2023年06月01日 11:38:18   作者:Ted劉  
Goroutine、Channel、Context、Sync都是Golang并發(fā)編程中的幾個(gè)重要組件,這篇文中主要為大家介紹了Goroutine的相關(guān)知識,需要的可以參考一下

并發(fā)指的是程序由若干個(gè)獨(dú)立運(yùn)行的代碼組成,主要依賴于多核CPU的并行運(yùn)算和調(diào)度能力。

Golang在并發(fā)方面的能力較為突出,通過Goroutinue實(shí)現(xiàn)了典型的協(xié)程的概念。Golang的并發(fā)理念是:通過通信來共享內(nèi)存,而不是通過共享內(nèi)存來通信。

goroutinue與傳統(tǒng)線程的區(qū)別

主要體現(xiàn)在四個(gè)方面:

1.內(nèi)存占用不同:Goroutinue的創(chuàng)建消耗2kb內(nèi)存,并且在??臻g不足時(shí)會自動(dòng)擴(kuò)容;而線程默認(rèn)會占用較大的??臻g(1-8MB),且棧空間大小不變,會有溢出風(fēng)險(xiǎn)

2.開銷不同:goroutinue創(chuàng)建和銷毀消耗都非常小,是用戶態(tài)線程;而線程的創(chuàng)建和銷毀都會有巨大的消耗,是內(nèi)核級交互

3.調(diào)度切換不同:goroutinue切換消耗200ns,在1.4后優(yōu)化至20ns;而線程切換消耗1000-15000納秒

4.復(fù)雜性不同:goroutinue簡單易用,M個(gè)線程托管N個(gè)goroutinue;線程創(chuàng)建和退出復(fù)雜,多個(gè)線程間通訊復(fù)雜,使用網(wǎng)絡(luò)多路復(fù)用,應(yīng)用服務(wù)線程門檻高

如果想實(shí)現(xiàn)一個(gè)并發(fā)程序,要考慮幾個(gè)方面:

程序代碼如何獨(dú)立運(yùn)行?

獨(dú)立運(yùn)行的代碼如何進(jìn)行通信?

如何做到數(shù)據(jù)同步、調(diào)度同步?

這就引出了Golang并發(fā)編程中的幾個(gè)重要組件:Goroutinue、Channel、Context、Sync

Goroutine

Golang中并發(fā)執(zhí)行的單元稱為Goroutinue,也就是Go協(xié)程

使用方法非常簡單,使用go關(guān)鍵字即可啟動(dòng)新的Goroutinue

示例代碼:

func main() {
    // 輸出奇數(shù)
    printOdd := func() {
        for i := 1; i <= 10; i += 2 {
            fmt.Println(i)
            time.Sleep(100 * time.Millisecond)
        }
    }

    // 輸出偶數(shù)
    printEven := func() {
        for i := 2; i <= 10; i += 2 {
            fmt.Println(i)
            time.Sleep(100 * time.Millisecond)
        }
    }

    go printOdd()
    go printEven()

    // 阻塞等待
    time.Sleep(time.Second)
}

執(zhí)行結(jié)果:

1 2 4 3 6 5 7 8 10 9 

我們只需要一個(gè)go關(guān)鍵字就可以非常簡便的啟動(dòng)一個(gè)Goroutinue協(xié)程。最后程序睡眠1秒,原因是主Goroutinue(main函數(shù))需要等待內(nèi)部Goroutinue運(yùn)行結(jié)束才能結(jié)束,否則子Goroutinue程序可能執(zhí)行一半會被強(qiáng)制停止。

調(diào)度的隨機(jī)性

通過結(jié)果可以看到數(shù)字的輸出順序并不是按照一定順序,因?yàn)镚oroutinue的調(diào)度執(zhí)行是隨機(jī)的。

Goroutinue的并發(fā)規(guī)模

goroutinue本身的數(shù)量是無上限的,但是一定會受到棧內(nèi)存空間以及操作系統(tǒng)的資源限制,可以通過函數(shù) runtime.NumGoroutine()獲取當(dāng)前Goroutinue數(shù)量。前面也提到過,一個(gè)Goroutinue初始的棧內(nèi)存只有2KB,用于保存Goroutinue中的執(zhí)行數(shù)據(jù),且棧內(nèi)存可以擴(kuò)容,按需增大或縮小,單個(gè)Goroutinue最大可以擴(kuò)展到1GB。

上面通過time sleep的方法太傻了,我們可以通過官方提供的 sync.WaitGroup 來實(shí)現(xiàn)Goroutinue的協(xié)同調(diào)度。

sync.WaitGroup

sync.WaitGroup用于等待一組Goroutinue執(zhí)行完畢,其實(shí)是一個(gè)計(jì)數(shù)器思想的實(shí)現(xiàn)方案,它的核心方法有三個(gè):

  • Add():調(diào)用此函數(shù)用于增加等待的Goroutinue數(shù)量,原子操作保證并發(fā)安全
  • Done():調(diào)用此函數(shù)用于減去一個(gè)計(jì)數(shù),原子操作保證并發(fā)安全
  • Wait():調(diào)用此函數(shù)用于阻塞,直到所有的Goroutinue完成,也就是計(jì)數(shù)器歸0時(shí),才會解除阻塞狀態(tài)

現(xiàn)在我們將上面的代碼最后一行的 time.Sleep(time.Second) 去掉,再次執(zhí)行會得到空的輸出,原因就是主Goroutinue直接結(jié)束,兩個(gè)子Goroutinue沒來得及執(zhí)行就已經(jīng)退出了,讓我們用 WaitGroup 來改造一下

示例代碼:

func main() {
    wg := sync.WaitGroup{}
    // 輸出奇數(shù)
    printOdd := func() {
        defer wg.Done()
        for i := 1; i <= 10; i += 2 {
            fmt.Printf("%d ", i)
            time.Sleep(100 * time.Millisecond)
        }
    }
    // 輸出偶數(shù)
    printEven := func() {
        defer wg.Done()
        for i := 2; i <= 10; i += 2 {
            fmt.Printf("%d ", i)
            time.Sleep(100 * time.Millisecond)
        }
    }
    wg.Add(2)
    go printOdd()
    go printEven()
    // 阻塞等待
    fmt.Println("waiting...")
    wg.Wait()
    fmt.Println("\nfinish...")
}

執(zhí)行結(jié)果:

waiting...
2 1 3 4 6 5 7 8 9 10 
finish...

這個(gè)簡單的例子可以比較直觀的展示waitGroup的基礎(chǔ)用法。waitGroup 適用于一個(gè)主Goroutinue需要等待其他Goroutinue全部運(yùn)行結(jié)束后才結(jié)束的這種場景,不適用于主Goroutinue需要結(jié)束,而通知其他Goroutinue結(jié)束的情景。

在這里有個(gè)使用上的注意事項(xiàng),那就是 waitGroup 不要復(fù)制使用,因?yàn)閮?nèi)部維護(hù)的計(jì)數(shù)器不能修改,否則會造成Goroutinue的泄露,在傳值時(shí)需要用指針類型來進(jìn)行傳遞。

waitGroup的內(nèi)部結(jié)構(gòu)

可以進(jìn)入源碼查看內(nèi)部結(jié)構(gòu):

type WaitGroup struct {
   noCopy noCopy
   // 64-bit value: high 32 bits are counter, low 32 bits are waiter count.
   // 64-bit atomic operations require 64-bit alignment, but 32-bit
   // compilers only guarantee that 64-bit fields are 32-bit aligned.
   // For this reason on 32 bit architectures we need to check in state()
   // if state1 is aligned or not, and dynamically "swap" the field order if
   // needed.
   state1 uint64
   state2 uint32
}

可以看到并不是一個(gè)復(fù)雜的結(jié)構(gòu),其中含義:

  • noCopy: 用于保證不會被復(fù)制
  • state1: 以64bit計(jì)算機(jī)為例,高32bit是計(jì)數(shù)器
  • state2: 以64bit計(jì)算機(jī)為例,低32bit是等待的Goroutinue

三大關(guān)鍵函數(shù)核心代碼:

func (wg *WaitGroup) Add(delta int) {
   ...
   state := atomic.AddUint64(statep, uint64(delta)<<32)
   ...
}
func (wg *WaitGroup) Done() {
   wg.Add(-1)
}
func (wg *WaitGroup) Wait() {
   ...
   for {
      state := atomic.LoadUint64(statep)
      v := int32(state >> 32)
      w := uint32(state)
      if v == 0 {
         // Counter is 0, no need to wait.
         if race.Enabled {
            race.Enable()
            race.Acquire(unsafe.Pointer(wg))
         }
         return
      }
      // Increment waiters count.
      if atomic.CompareAndSwapUint64(statep, state, state+1) {
         if race.Enabled && w == 0 {
            // Wait must be synchronized with the first Add.
            // Need to model this is as a write to race with the read in Add.
            // As a consequence, can do the write only for the first waiter,
            // otherwise concurrent Waits will race with each other.
            race.Write(unsafe.Pointer(semap))
         }
         runtime_Semacquire(semap)
         if *statep != 0 {
            panic("sync: WaitGroup is reused before previous Wait has returned")
         }
         if race.Enabled {
            race.Enable()
            race.Acquire(unsafe.Pointer(wg))
         }
         return
      }
   }
}

到此這篇關(guān)于Golang并發(fā)繞不開的重要組件之Goroutine詳解的文章就介紹到這了,更多相關(guān)Golang Goroutine內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Golang泛型的使用方法詳解

    Golang泛型的使用方法詳解

    這篇文章主要介紹了Golang中泛型的使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • golang sync.Pool 指針數(shù)據(jù)覆蓋問題解決

    golang sync.Pool 指針數(shù)據(jù)覆蓋問題解決

    本文主要介紹了使用sync.Pool時(shí)遇到指針數(shù)據(jù)覆蓋的問題,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-03-03
  • Go語言中如何通過方法為類型添加行為

    Go語言中如何通過方法為類型添加行為

    這篇文章主要介紹了Go語言中如何通過方法為類型添加行為的相關(guān)資料,文中通過圖文介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • Golang熔斷器的開發(fā)過程詳解

    Golang熔斷器的開發(fā)過程詳解

    Golang熔斷器是一種用于處理分布式系統(tǒng)中服務(wù)調(diào)用的故障保護(hù)機(jī)制,它可以防止故障服務(wù)的連鎖反應(yīng),提高系統(tǒng)的穩(wěn)定性和可靠性,本文將給大家詳細(xì)的介紹一下Golang熔斷器的開發(fā)過程,需要的朋友可以參考下
    2023-09-09
  • 學(xué)會提升Go語言編碼效率技巧拒絕加班!

    學(xué)會提升Go語言編碼效率技巧拒絕加班!

    這篇文章主要為大家介紹了Go語言編碼效率提升技巧詳解,學(xué)會了從此拒絕加班,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • 深入解析Golang中JSON的編碼與解碼

    深入解析Golang中JSON的編碼與解碼

    隨著互聯(lián)網(wǎng)的快速發(fā)展和數(shù)據(jù)交換的廣泛應(yīng)用,各種數(shù)據(jù)格式的處理成為軟件開發(fā)中的關(guān)鍵問題,本文將介紹?Golang?中?JSON?編碼與解碼的相關(guān)知識,幫助大家了解其基本原理和高效應(yīng)用,需要的可以收藏一下
    2023-05-05
  • Go Java算法之單詞規(guī)律示例詳解

    Go Java算法之單詞規(guī)律示例詳解

    這篇文章主要為大家介紹了Go Java算法之單詞規(guī)律示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • Go語言使用時(shí)會遇到的錯(cuò)誤及解決方法詳解

    Go語言使用時(shí)會遇到的錯(cuò)誤及解決方法詳解

    這篇文章主要為大家詳細(xì)介紹了Go語言使用時(shí)常常會遇到的一些錯(cuò)誤及解決方法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下
    2023-07-07
  • go開發(fā)alertmanger實(shí)現(xiàn)釘釘報(bào)警

    go開發(fā)alertmanger實(shí)現(xiàn)釘釘報(bào)警

    本文主要介紹了go開發(fā)alertmanger實(shí)現(xiàn)釘釘報(bào)警,通過自己的url實(shí)現(xiàn)alertmanager的釘釘報(bào)警,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • 重學(xué)Go語言之如何開發(fā)RPC應(yīng)用

    重學(xué)Go語言之如何開發(fā)RPC應(yīng)用

    這篇文章主要為大家詳細(xì)介紹了在Go語言中如何構(gòu)建RPC應(yīng)用,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-09-09

最新評論