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

Go語(yǔ)言中的并發(fā)模式你了解了嗎

 更新時(shí)間:2023年08月30日 09:35:08   作者:阿兵云原生  
工作中查看項(xiàng)目代碼,發(fā)現(xiàn)會(huì)存在使用?GO?語(yǔ)言做并發(fā)的時(shí)候出現(xiàn)各種各樣的異常情況,實(shí)際上,出現(xiàn)上述的情況,還是因?yàn)槲覀儗?duì)于?GO?語(yǔ)言的并發(fā)模型和涉及的?GO?語(yǔ)言基礎(chǔ)不夠扎實(shí),所以本文小編就來(lái)帶大家深入了解下Go語(yǔ)言中的并發(fā)模式吧

工作中查看項(xiàng)目代碼,發(fā)現(xiàn)會(huì)存在使用 GO 語(yǔ)言做并發(fā)的時(shí)候出現(xiàn)各種各樣的異常情況,有的輸出結(jié)果和自己期望和設(shè)計(jì)的不一致,有的是程序直接阻塞住,更有甚者直接是程序 crash 掉。

實(shí)際上,出現(xiàn)上述的情況,還是因?yàn)槲覀儗?duì)于 GO 語(yǔ)言的并發(fā)模型和涉及的 GO 語(yǔ)言基礎(chǔ)不夠扎實(shí),誤解了語(yǔ)言的用法。

那么,對(duì)于 GO 語(yǔ)言的并發(fā)模式,我們一起來(lái)梳理一波。 GO 語(yǔ)言常見(jiàn)的并發(fā)模式有這些:

  • 創(chuàng)建模式
  • 退出模式
  • 管道模式
  • 超時(shí)模式和取消模式

在 GO 語(yǔ)言里面,咱們使用使用并發(fā),自然離不開(kāi)使用 GO 語(yǔ)言的協(xié)程 goroutine,通道 channel 和 多路復(fù)用 select,接下來(lái)就來(lái)看看各種模式都是如何去搭配使用這三個(gè)關(guān)鍵原語(yǔ)的

創(chuàng)建模式

使用過(guò)通道和協(xié)程的朋友對(duì)于創(chuàng)建模式肯定不會(huì)模式,這是一個(gè)非常常用的方式,也是一個(gè)非常簡(jiǎn)單的使用方式:

  • 主協(xié)程中調(diào)用 help 函數(shù),返回一個(gè)通道 ch 變量
  • 通道 ch 用于主協(xié)程和 子協(xié)程之間的通信,其中通道的數(shù)據(jù)類(lèi)型完全可以自行定義
type XXX struct{...}
func help(fn func()) chan XXX {
    ch := make(chan XXX)
    // 開(kāi)啟一個(gè)協(xié)程
    go func(){
        // 此處的協(xié)程可以控制和外部的 主協(xié)程 通過(guò) ch 來(lái)進(jìn)行通信,達(dá)到一定邏輯便可以執(zhí)行自己的 fn 函數(shù)
        fn()
        ch <- XXX
    }()
}
func main(){
    ch := help(func(){
        fmt.Println("這是GO 語(yǔ)言 并發(fā)模式之 創(chuàng)建模式")
    })
    <- ch
}

退出模式

程序的退出我們應(yīng)該也不會(huì)陌生,對(duì)于一些常駐的服務(wù),如果是要退出程序,自然是不能直接就斷掉,此時(shí)會(huì)有一些連接和業(yè)務(wù)并沒(méi)有關(guān)閉,直接關(guān)閉程序會(huì)導(dǎo)致業(yè)務(wù)異常,例如在關(guān)閉過(guò)程中最后一個(gè) http 請(qǐng)求沒(méi)有正常響應(yīng)等等等

此時(shí),就需要做優(yōu)雅關(guān)閉了,對(duì)于協(xié)程 goroutine 退出有 3 種模式

  • 分離模式
  • join 模式
  • notify-and-wait 模式

分離模式

此處的分離模式,分離這個(gè)術(shù)語(yǔ)實(shí)際上是線程中的術(shù)語(yǔ),pthread detached

分離模式可以理解為,咱們創(chuàng)建的協(xié)程 goroutine,直接分離,創(chuàng)建子協(xié)程的父協(xié)程不用關(guān)心子協(xié)程是如何退出的,子協(xié)程的生命周期主要與它執(zhí)行的主函數(shù)有關(guān),咱們 return 之后,子協(xié)程也就結(jié)束了

對(duì)于這類(lèi)分離模式的協(xié)程,咱們需要關(guān)注兩類(lèi),一種是一次性的任務(wù),咱們 go 出來(lái)后,執(zhí)行簡(jiǎn)單任務(wù)完畢后直接退出,一種是常駐程序,需要優(yōu)雅退出,處理一些垃圾回收的事情

例如這樣:

  • 主程序中設(shè)置一個(gè)通道變量 ch ,類(lèi)型為 os.Signal
  • 然后主程序就開(kāi)始各種創(chuàng)建協(xié)程執(zhí)行自己的各種業(yè)務(wù)
  • 直到程序收到了 syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT 任意一個(gè)信號(hào)的時(shí)候,則會(huì)開(kāi)始進(jìn)行垃圾回收等清理工作,執(zhí)行完畢后,程序再進(jìn)行退出
func main(){
     ch := make(chan os.Signal)
     signal.Notify(c, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
     // ...
     // go 程序執(zhí)行其他業(yè)務(wù)
     // ...
    for i := range ch {
        switch i {
        case syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT:
            // 做一些清理工作
            os.Exit(0)
        }
    }
}

join 模式

看到這個(gè)關(guān)鍵字,是不是也似曾相識(shí),和線程貌似很像,例如 線程中 父線程可以通過(guò) pthread_join 來(lái)等待子線程結(jié)束,并且還可以獲取子線程的結(jié)束狀態(tài)

GO 語(yǔ)言中等待子協(xié)程退出并且獲取子協(xié)程的退出狀態(tài),咱們就可以使用通道 channel 的方式來(lái)進(jìn)行處理

例子1

等待一個(gè)子協(xié)程退出,并獲取退出狀態(tài)

  • 主協(xié)程中調(diào)用 help 方法得到一個(gè) ch 通道變量,主協(xié)程阻塞著讀 ch
  • help 中開(kāi)辟一個(gè)子協(xié)程去執(zhí)行傳入的 fn 回調(diào)函數(shù),并傳參為 ok bool
  • 實(shí)際 fn 函數(shù)判斷傳參 ok 是否是 true,若不是則返回具體的錯(cuò)誤信息,若是 true 則返回 nil
func help(f func(bool) error, ok bool) <-chan error {
	ch := make(chan error)
	go func() {
		ch <- f(ok)
	}()
	return ch
}
func fn(ok bool) error {
	if !ok {
		return errors.New("not ok ... ")
	}
	return nil
}
func main() {
	ch := help(fn, true)
	fmt.Println("help 111")
	err := <-ch
	fmt.Println("help 111 done ", err)
	ch = help(fn, false)
	fmt.Println("help 222")
	err = <-ch
	fmt.Println("help 222 done ", err)
}

看上如上程序,我們就可以知道,第一次調(diào)用 help(fn , true) ,主協(xié)程等待子協(xié)程退出的時(shí)候,會(huì)得到一個(gè)錯(cuò)誤信息,為 not ok ... , 第二次調(diào)用 help(fn , false) 的時(shí)候,返回的 err 是一個(gè) nil

通過(guò)上述這種方式,主協(xié)程不僅可以輕易的等待一個(gè)子協(xié)程退出,還可以獲取到子協(xié)程退出的狀態(tài)

那么,主協(xié)程如果是等待多個(gè)協(xié)程退出呢?需要如何處理?

例子2

主協(xié)程等待多個(gè)協(xié)程退出咱們就需要使用到 GO 中的 sync.WaitGroup

  • 使用 help 函數(shù),傳入回調(diào)函數(shù),參數(shù)1 bool,參數(shù)2 int ,其中參數(shù) 2 表示開(kāi)辟子協(xié)程的個(gè)數(shù),返回值為一個(gè)無(wú)緩沖的 channel 變量,數(shù)據(jù)類(lèi)型是 struct{}
  • 使用 var wg sync.WaitGroup ,開(kāi)辟子協(xié)程的時(shí)候記錄一次 wg.Add(1),當(dāng)子協(xié)程退出時(shí) ,記錄退出 wg.Done()
  • help 中再另起一個(gè)協(xié)程 wg.Wait() 等待所有子協(xié)程退出,并將 ch 變量寫(xiě)入值
  • 主協(xié)程阻塞讀取 ch 變量的值,待所有子協(xié)程都退出之后,help 中寫(xiě)入到 ch 中的數(shù)據(jù),主協(xié)程就能馬上收到 ch 中的數(shù)據(jù),并退出程序
func help(f func(bool)error, ok bool, num int)chan struct{}{
    ch := make(chan struct{})
    var wg sync.WaitGroup
    for i:=0; i<num; i++ {
        wg.Add(1)
        go func(){
            f(ok)
            fmt.Println(" f done ")
            wg.Done()
        }() 
    }
    go func(){
        // 等待所有子協(xié)程退出
        wg.Wait()
        ch <- struct{}{}
    }()
    return ch
}
func fn(ok bool) error{
    time.Sleep(time.Second * 1)
    if !ok{
        return errors.New("not ok ... ")
    }
    return nil
}
func main(){
    ch := help(fn , true)
    fmt.Println("help 111")
     <- ch 
    fmt.Println("help 111 done ",err)
}

notify-and-wait 模式

可以看到上述模式,都是主協(xié)程等待一個(gè)子協(xié)程,或者多個(gè)子協(xié)程結(jié)束后,主協(xié)程再進(jìn)行退出,或者處理完垃圾回收后退出

那么如果主協(xié)程要主動(dòng)通知子協(xié)程退出,我們應(yīng)該要如何處理呢?

同樣的問(wèn)題,如果主協(xié)程自己退出了,而沒(méi)有通知其他子協(xié)程退出,這是會(huì)導(dǎo)致業(yè)務(wù)數(shù)據(jù)異?;蛘邅G失的,那么此刻我們就可以使用到 notify-and-wait 模式 來(lái)進(jìn)行處理

我們就直接來(lái)寫(xiě)一個(gè)主協(xié)程通知并等待多個(gè)子協(xié)程退出的 demo:

  • 主協(xié)程調(diào)用 help 函數(shù),得到一個(gè) quit chan struct{} 類(lèi)型的通道變量,主協(xié)程阻塞讀取 quit 的值
  • help 函數(shù)根據(jù)傳入的參數(shù) num 來(lái)創(chuàng)建 num 個(gè)子協(xié)程,并且使用 sync.WaitGroup 來(lái)控制
  • 當(dāng)主協(xié)程在 quit 通道中寫(xiě)入數(shù)據(jù)時(shí),主動(dòng)通知所有子協(xié)程退出
  • help 中的另外一個(gè)協(xié)程讀取到 quit 通道中的數(shù)據(jù),便 close 掉 j 通道,觸發(fā)所有的子協(xié)程讀取 j 通道值的時(shí)候,得到的 ok 為 false,進(jìn)而所有子協(xié)程退出
  • wg.Wait() 等待所有子協(xié)程退出后,再在 quit 中寫(xiě)入數(shù)據(jù)
  • 主協(xié)程此時(shí)從 quit 中讀取到數(shù)據(jù),則知道所有子協(xié)程全部退出,自己的主協(xié)程即刻退出
func fn(){
   // 模擬在處理業(yè)務(wù)
   time.Sleep(time.Second * 1)
}
func help(num int, f func()) chan struct{}{
   quit := make(chan struct{})
   j := make(chan int)
   var wg sync.WaitGroup
   // 創(chuàng)建子協(xié)程處理業(yè)務(wù)
   for i:=0;i<num;i++{
      wg.Add(1)
      go func(){
         defer wg.Done()
         _,ok:=<-j
         if !ok{
            fmt.Println("exit child goroutine .")
            return
         }
         // 子協(xié)程 正常執(zhí)行業(yè)務(wù)
         f()
      }()
   }
   go func(){
      <-quit
      close(j)
      // 等待子協(xié)程全部退出
      wg.Wait()
      quit <- struct{}{}
   }()
   return quit
}
func main(){
   quit := help(10, fn)
   // 模擬主程序處理在處理其他事項(xiàng)
   // ...
   time.Sleep(time.Second * 10)
   quit <- struct{}{}
   // 此處等待所有子程序退出
   select{
   case <- quit:
      fmt.Println(" programs exit. ")
   }
}

上述程序執(zhí)行結(jié)果如下,可以看到 help 函數(shù)創(chuàng)建了 10 個(gè)子協(xié)程,主協(xié)程主動(dòng)通知子協(xié)程全部退出,退出的時(shí)候也是 10 個(gè)子協(xié)程退出了,主協(xié)程才退出

上述程序,如果某一個(gè)子協(xié)程出現(xiàn)了問(wèn)題,導(dǎo)致子協(xié)程不能完全退出,也就是說(shuō)某些子協(xié)程在 f 函數(shù)中阻塞住了,那么這個(gè)時(shí)候主協(xié)程豈不是一直無(wú)法退出???

那么此時(shí),在主協(xié)程通知子協(xié)程退出的時(shí)候,我們加上一個(gè)超時(shí)時(shí)間,表達(dá)意思為,超過(guò)某個(gè)時(shí)間,如果子協(xié)程還沒(méi)有全部退出完畢,那么主協(xié)程仍然主動(dòng)關(guān)閉程序,可以這樣寫(xiě):

設(shè)定一個(gè)定時(shí)器, 3 秒后會(huì)觸發(fā),即可以從 t.C 中讀取到數(shù)據(jù)

t := time.NewTimer(time.Second * 3)
defer t.Stop()
// 此處等待所有子程序退出
select{
case <-t.C:
   fmt.Println("timeout programs exit. ")
case <- quit:
   fmt.Println(" 111 programs exit. ")
}

管道模式

說(shuō)到管理,或許大家對(duì) linux 里面的管道更加熟悉吧,例如使用 linux 命令找到文件中的 golang 這個(gè)字符串

cat xxx.txt |grep "golang"

那么對(duì)于 GO 語(yǔ)言并發(fā)模式中的管道模式也是類(lèi)似的效果,我們就可以用這個(gè)管道模式來(lái)過(guò)濾數(shù)據(jù)

例如我們可以設(shè)計(jì)這樣一個(gè)程序,兄弟們可以動(dòng)起手來(lái)寫(xiě)一寫(xiě),評(píng)論區(qū)見(jiàn)哦:

  • 整個(gè)程序總共使用 2 個(gè)通道
  • help 函數(shù)中傳輸數(shù)據(jù)量 50 ,邏輯計(jì)算能夠被 5 整除的數(shù)據(jù)寫(xiě)到第一個(gè)通道 ch1 中
  • 另一個(gè)協(xié)程阻塞讀取 ch1 中的內(nèi)容,并將取出的數(shù)據(jù)乘以 3 ,將結(jié)果寫(xiě)入到 ch2 中
  • 主協(xié)程就阻塞讀取 ch2 的內(nèi)容,讀取到內(nèi)容后,挨個(gè)打印出來(lái)

管道模式有兩種模式,扇出模式 和 扇入模式,這個(gè)比較好理解

  • 扇出模式:多種類(lèi)型的數(shù)據(jù)從同一個(gè)通道 channel 中讀取數(shù)據(jù),直到通道關(guān)閉
  • 扇入模式:輸入的時(shí)候有多個(gè)通道channel,程序?qū)⑺械耐ǖ纼?nèi)數(shù)據(jù)匯聚,統(tǒng)一輸入到另外一個(gè)通道channel A 里面,另外一個(gè)程序則從這個(gè)通道channel A 中讀取數(shù)據(jù),直到這個(gè)通道A關(guān)閉為止

超時(shí)模式和取消模式化

超時(shí)模式

上述例子中有專(zhuān)門(mén)說(shuō)到如何去使用他,實(shí)際上我們還可以這樣用:

select{
case <- time.Afer(time.Second * 2):
   fmt.Println("timeout programs exit. ")
case <- quit:
   fmt.Println(" 111 programs exit. ")
}

取消模式

則是使用了 GO 語(yǔ)言的 context 包中的提供了上下文機(jī)制,可以在協(xié)程 goroutine 之間傳遞 deadline,取消等信號(hào)

我們使用的時(shí)候例如可以這樣:

  • 使用 context.WithCancel 創(chuàng)建一個(gè)可以被取消的上下文,啟動(dòng)一個(gè)協(xié)程 在 3 秒后關(guān)閉上下文
  • 使用 for 循環(huán)模擬處理業(yè)務(wù),默認(rèn)會(huì)走 select 的 default 分支
  • 3 秒后 走到 select 的 ctx.Done(),則進(jìn)入到了取消模式,程序退出
ctx, cancelFunc := context.WithCancel(context.Background())
go func() {
   time.Sleep(time.Second * 3)
   cancelFunc()
}()
for {
   select {
   case <-ctx.Done():
      fmt.Println("program exit .")
      return
   default:
      fmt.Println("I'm still here.")
      time.Sleep(time.Second)
   }
}

總的來(lái)說(shuō),今天分享了 GO 語(yǔ)言中常見(jiàn)的幾種并發(fā)模式:創(chuàng)建模式,退出模式,管道模式,超時(shí)模式和取消模式,更多的,還是要我們要思考其原理和應(yīng)用起來(lái),學(xué)習(xí)他們才能更加的有效

以上就是Go語(yǔ)言中的并發(fā)模式你了解了嗎的詳細(xì)內(nèi)容,更多關(guān)于go并發(fā)模式的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • GoLang sync.Pool簡(jiǎn)介與用法

    GoLang sync.Pool簡(jiǎn)介與用法

    這篇文章主要介紹了GoLang sync.Pool簡(jiǎn)介與用法,Pool是可伸縮、并發(fā)安全的臨時(shí)對(duì)象池,用來(lái)存放已經(jīng)分配但暫時(shí)不用的臨時(shí)對(duì)象,通過(guò)對(duì)象重用機(jī)制,緩解GC壓力,提高程序性能
    2023-01-01
  • Go語(yǔ)言遍歷循環(huán)的幾種方法

    Go語(yǔ)言遍歷循環(huán)的幾種方法

    遍歷循環(huán)主要用于迭代數(shù)組、切片、映射(map)、字符串等數(shù)據(jù)結(jié)構(gòu),本文主要介紹了Go語(yǔ)言遍歷循環(huán)的幾種方法,下面就來(lái)介紹一下,具有一定的參考價(jià)值,感興趣的可以了解一下
    2025-03-03
  • Golang中自定義json序列化時(shí)間格式的示例代碼

    Golang中自定義json序列化時(shí)間格式的示例代碼

    Go語(yǔ)言作為一個(gè)由Google開(kāi)發(fā),號(hào)稱(chēng)互聯(lián)網(wǎng)的C語(yǔ)言的語(yǔ)言,自然也對(duì)JSON格式支持很好,下面這篇文章主要介紹了關(guān)于Golang中自定義json序列化時(shí)間格式的相關(guān)內(nèi)容,下面話不多說(shuō)了,來(lái)一起看看詳細(xì)的介紹吧
    2024-08-08
  • 基于gin的golang web開(kāi)發(fā)之認(rèn)證利器jwt

    基于gin的golang web開(kāi)發(fā)之認(rèn)證利器jwt

    這篇文章主要介紹了基于gin的golang web開(kāi)發(fā)之認(rèn)證利器jwt,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-12-12
  • Hugo 游樂(lè)場(chǎng)內(nèi)容初始化示例詳解

    Hugo 游樂(lè)場(chǎng)內(nèi)容初始化示例詳解

    這篇文章主要為大家介紹了Hugo 游樂(lè)場(chǎng)內(nèi)容初始化示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • Go讀取文件與寫(xiě)入文件的三種方法操作指南

    Go讀取文件與寫(xiě)入文件的三種方法操作指南

    在 Go 語(yǔ)言中也經(jīng)常會(huì)遇到操作文件的需求,下面這篇文章主要給大家介紹了關(guān)于Go讀取文件與寫(xiě)入文件的三種方法操作,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-09-09
  • golang testing使用示例小結(jié)

    golang testing使用示例小結(jié)

    golang自帶了testing測(cè)試包,使用該包可以進(jìn)行自動(dòng)化的單元測(cè)試,輸出結(jié)果驗(yàn)證,并且可以測(cè)試性能,下面通過(guò)示例代碼給大家講解golang testing使用示例小結(jié),感興趣的朋友一起看看吧
    2024-05-05
  • golang官方嵌入文件到可執(zhí)行程序的示例詳解

    golang官方嵌入文件到可執(zhí)行程序的示例詳解

    這篇文章主要介紹了golang官方嵌入文件到可執(zhí)行程序,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-02-02
  • 減少 golang 二進(jìn)制文件大小操作

    減少 golang 二進(jìn)制文件大小操作

    這篇文章主要介紹了減少 golang 二進(jìn)制文件大小操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-12-12
  • Go語(yǔ)言中的UTF-8實(shí)現(xiàn)

    Go語(yǔ)言中的UTF-8實(shí)現(xiàn)

    這篇文章主要介紹了Go語(yǔ)言中的UTF-8實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04

最新評(píng)論