精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

講完Go并發(fā)控制,講講并發(fā)抑制

開發(fā) 前端
在高并發(fā)的狀態(tài)下,一般會給熱點數(shù)據(jù)設置緩存。但數(shù)據(jù)第一次訪問或者緩存失效的狀態(tài)下,如果直接去查詢數(shù)據(jù)庫,會給數(shù)據(jù)庫造成極大壓力,甚至直接打爆數(shù)據(jù)庫。

已知有一個函數(shù)search,能夠按照關鍵詞執(zhí)行搜索,coSearch能夠批量并發(fā)查詢。

讓我們把目光定位到search上,search通過查詢數(shù)據(jù)庫或者調(diào)用其他api來完成搜索,這是一個相對耗時和消耗資源的操作。

當多個相同的關鍵詞并發(fā)查詢(調(diào)用search函數(shù))時,我們希望只產(chǎn)生一次數(shù)據(jù)庫調(diào)用(調(diào)用query),第一個查詢未完成時后續(xù)的重復查詢會等待,當?shù)谝粋€查詢完成時則會與其他查詢分享結(jié)果,這樣一來雖然只執(zhí)行了一次數(shù)據(jù)庫調(diào)用但是所有查詢都拿到了最終的結(jié)果。

圖片圖片

什么是并發(fā)抑制:

package main

import (
 "context"
 "fmt"
 "sync"
 "time"

 "golang.org/x/sync/errgroup"
)

func query(ctx context.Context, word string) (string, error) {
 fmt.Println("searching: ", word)
 time.Sleep(5 * time.Second)

 return fmt.Sprintf("result: %s", word), nil // 模擬結(jié)果
}

// 實現(xiàn)search,在重復并發(fā)調(diào)用下僅執(zhí)行一次query
// 其他并發(fā)共享這次query的結(jié)果
func search(ctx context.Context, word string) (string, error) {
    return query(ctx, word)
}

func coSearch(ctx context.Context, words []string) ([]string, error) {
 g, ctx := errgroup.WithContext(ctx)
 g.SetLimit(10)

 results := make([]string, len(words))

 for i, word := range words {
  i, word := i, word

  g.Go(func() error {
   result, err := search(ctx, word)
   if err != nil {
    return err
   }

   results[i] = result
   return nil
  })
 }

 err := g.Wait()

 return results, err
}

func main() {
 words := []string{"Go","Go", "Go", "Rust", "PHP", "JavaScript", "Java"}
 results, err := coSearch(context.Background(), words)
 if err != nil {
  fmt.Println(err)
  return
 }

 fmt.Println(results)
}

好了,可以先暫停想想該如何實現(xiàn)search函數(shù)了。

一步一步實現(xiàn)并發(fā)抑制

我們先假設所有查詢關鍵詞都一樣,那么問題簡化成并發(fā)執(zhí)行search時,只在第一次search時調(diào)用query,其他的search并發(fā)調(diào)用等待并共享這次的查詢結(jié)果。

通過waiting變量,其他goroutine等待第一個goroutine數(shù)據(jù)庫調(diào)用完成,那么如何讓其他goroutine等待在這個位置呢?

func main() {
 words := []string{"Go", "Go", "Go", "Go", "Go"}
 results, err := coSearch(context.Background(), words)
 if err != nil {
  fmt.Println(err)
  return
 }

 fmt.Println(results)
}

var (
 waiting bool
 resp    string
 err     error
)

func search(ctx context.Context, word string) (string, error) {
  if waiting {
    // 等待resp, err被賦值,即第一個query完成后再返回
    // ...?
      return resp, err
  }

  waiting = true
  resp, err = query(ctx, word)
  waiting = false

  return resp, err
}

func query(ctx context.Context, word string) (string, error) {
 fmt.Println("searching: ", word)
 time.Sleep(5 * time.Second)

 return fmt.Sprintf("result: %s", word), nil // 模擬結(jié)果
}

sync.WaitGroup{}并發(fā)控制

sync.WaitGroup{}是并發(fā)控制的核心,這里再次重申下用法:

  • 當新運行一個goroutine時,我們需要調(diào)用wg.Add(1)。
  • 當一個goroutine運行完成的時候,我們需要調(diào)用wg.Done()。
  • wg.Wait()讓程序阻塞在此處,直到所有的goroutine運行完畢。

利用 sync.WaitGroup{}便可實現(xiàn)上文代碼中等待的效果:

var (
 wg      sync.WaitGroup
 waiting bool
 resp    string
 err     error
)

func search(ctx context.Context, word string) (string, error) {
 if waiting {
  // 其他goroutine等待第一個goroutine執(zhí)行完成
  wg.Wait()
  return resp, err
 }

 waiting = true
    
 wg.Add(1)
 resp, err = query(ctx, word)
    wg.Done() // 第一個goroutine執(zhí)行完成
    
 waiting = false

 return resp, err
}

并發(fā)安全

當多個goroutine對同一個內(nèi)存區(qū)域進行讀寫時,就會產(chǎn)生并發(fā)安全的問題,它會導致程序運行的結(jié)果不符合預期,而上文的程序并發(fā)的讀寫了waiting變量,需要給waiting變量加把鎖。

釋放鎖的位置非常的有技巧,如果在在wg.Add(1)之前mu.Unlock(),可能 wg.Add(1)還未來得執(zhí)行其他goroutine已經(jīng)執(zhí)行了wg.Wait(),并獲取到了錯誤的數(shù)據(jù)。

unlock在add之前;

var (
  wg      sync.WaitGroup
  mu      sync.Mutex
  waiting bool
  resp    string
  err     error
)

func search(ctx context.Context, word string) (string, error) {
 mu.Lock()

 if waiting {
  mu.Unlock()
  wg.Wait()
  return resp, err
 }

 waiting = true
    
 wg.Add(1)
    // 在wg.Add(1)之后釋放鎖,保證其他goroutine被wg.Wait()阻塞
 mu.Unlock()

 resp, err = query(ctx, word)
    wg.Done()

 mu.Lock()
 waiting = false
 mu.Unlock() 
    
 return resp, err
}

完整版本

現(xiàn)在可以針對不同的關鍵詞做區(qū)分了,使用一個map來代替原有的waiting,并將每一個關鍵詞查詢的WaitGroup和結(jié)果打包到map的value中。

type call struct {
 wg   sync.WaitGroup
 resp string
 err  error
}

var (
    mu sync.Mutex
    m = make(map[string]*call)
)

func search(ctx context.Context, word string) (string, error) {
 mu.Lock()

 if c, ok := m[word]; ok {
  mu.Unlock()
  c.wg.Wait()
  return c.resp, c.err
 }

 c := &call{}
 m[word] = c

 c.wg.Add(1)
 // 在wg.Add(1)之后才釋放鎖,保證其他goroutine被wg.Wait()阻塞
 mu.Unlock()

 c.resp, c.err = query(ctx, word)
 c.wg.Done()

 mu.Lock()
 delete(m, word)
 mu.Unlock()

 return c.resp, c.err
}

開源庫 golang.org/x/sync/singleflight

上面一步一步教大家手搓了一個并發(fā)抑制的邏輯,我們的基本邏輯和開源庫golang.org/x/sync/singleflight沒有區(qū)別,只是singleflight內(nèi)部實現(xiàn)更加嚴謹

直接使用singleflight非常簡單的就可以實現(xiàn)我們的訴求

  • singleflight.Group 創(chuàng)建一個需要并發(fā)控制的范圍
  • Do函數(shù)

第一個參數(shù)接收一個key來判斷否重復調(diào)用

第二個參數(shù)為要執(zhí)行的函數(shù),函數(shù)可以返回正常值或者error

Do函數(shù)返回值除了閉包函數(shù)的返回值之外,還返回了此次返回值是否由其他goroutine共享

import (
 "golang.org/x/sync/singleflight"
)

var g = new(singleflight.Group)

func search(ctx context.Context, word string) (string, error) {
 resp, err, _ := g.Do(word, func() (interface{}, error) {
  return query(ctx, word)
 })

 return resp.(string), err
}

錯誤處理

因為共享第一個goroutine的結(jié)果,因此如果第一次調(diào)用失敗,那其他goroutine也都會失敗

如果在某些場景下允許第一個調(diào)用失敗后再次嘗試調(diào)用該函數(shù),那么可以通過調(diào)用Forget方法來忘記這個key

var g = new(singleflight.Group)

func search(ctx context.Context, word string) (string, error) {
 resp, err, _ := g.Do(word, func() (interface{}, error) {
  val, err := query(ctx, word)
  // 當出錯并且允許重試時
  if err != nil && true {
   g.Forget(word)
   return "", err
  }

  return val, err
 })

 return resp.(string), err
}

超時控制

當使用Do函數(shù)時,如果query長時間未響應(這里假設qeury不具備超時能力),那么所有的goroutine都會被阻塞并等待,利用DoChan+select可以實現(xiàn)超時邏輯

var g = new(singleflight.Group)

func search(ctx context.Context, word string) (string, error) {
    ctx, cancel := context.WithTimeout(ctx, 2*time.Second)
 defer cancel()
    
 result := g.DoChan(word, func() (interface{}, error) {
  return query(ctx, word)
 })

 select {
 case r := <-result:
  return r.Val.(string), r.Err
 case <-ctx.Done():
  return "", ctx.Err()
 }
}

使用場景

預防緩存穿透

在高并發(fā)的狀態(tài)下,一般會給熱點數(shù)據(jù)設置緩存。但數(shù)據(jù)第一次訪問或者緩存失效的狀態(tài)下,如果直接去查詢數(shù)據(jù)庫,會給數(shù)據(jù)庫造成極大壓力,甚至直接打爆數(shù)據(jù)庫。

以上各種分享中被反復提到的場景,但!注意!使用singleflight就一勞永逸了么,不是的,在大規(guī)模集群下可能有數(shù)百臺機器,當處在高并發(fā)狀態(tài)時,即使每臺機器只發(fā)起一個請求,也足以打爆你的數(shù)據(jù)庫!結(jié)合實際,搭配適當?shù)木彺娌呗浴?shù)據(jù)預熱、限流等手段才能避免潛在的風險。挖個坑,以后有機會聊聊這些問題

總結(jié)

本篇作為一個例子,給你講透典型的Go并發(fā)控制的姊妹篇,講述了另外一種并發(fā)控制模型,并介紹了開源庫golang.org/x/sync/singleflight。

當由一個goroutine并發(fā)向下發(fā)展成多個goroutine時,使用golang.org/x/sync/errgroup

當多個goroutine并發(fā)向下抑制成一個goroutine時,使用golang.org/x/sync/singleflight

責任編輯:武曉燕 來源: 涼涼的知識庫
相關推薦

2023-01-30 15:41:10

Channel控制并發(fā)

2017-08-21 10:56:55

MySQL并發(fā)控制

2022-10-17 08:07:13

Go 語言并發(fā)編程

2022-12-12 09:07:06

Redis并發(fā)限流

2009-09-24 14:43:53

Hibernate樂觀

2021-07-28 08:32:58

Go并發(fā)Select

2025-06-03 02:00:00

2024-04-30 10:29:46

前端開發(fā)h5開發(fā)函數(shù)

2023-02-10 09:40:36

Go語言并發(fā)

2013-05-28 09:43:38

GoGo語言并發(fā)模式

2021-07-15 23:18:48

Go語言并發(fā)

2025-07-30 09:55:19

2025-05-22 09:01:28

2016-10-28 17:39:47

phpgolangcoroutine

2023-12-21 07:09:32

Go語言任務

2023-12-29 08:10:41

Go并發(fā)開發(fā)

2025-01-15 09:13:53

2021-07-06 14:47:30

Go 開發(fā)技術

2023-11-27 18:07:05

Go并發(fā)編程

2021-07-30 07:28:15

WorkerPoolGo語言
點贊
收藏

51CTO技術棧公眾號

黄色av电影网站| 天堂精品视频| 99久热在线精品996热是什么| 欧美中文一区| 欧美性极品xxxx娇小| 色视频一区二区三区| 国产精品久久久久久久久久久久久久久久| 欧美在线免费| 亚洲美女www午夜| 国产色视频在线播放| 欧美卡一卡二| 久久色.com| 91在线观看免费网站| 日韩av电影网| 国产精品久久久久久久久妇女| 精品国产a毛片| 美女少妇一区二区| 91福利区在线观看| 欧美激情自拍偷拍| 国产高清一区二区三区| 波多野结衣一二区| 国内精品久久久久久久影视蜜臀| 亚洲人成网7777777国产| 奇米视频888| 2022成人影院| 一区二区三区久久| 亚洲欧洲另类精品久久综合| 亚洲乱码在线观看| 麻豆久久一区二区| 欧美做受高潮1| 希岛爱理中文字幕| 久久99视频| 337p日本欧洲亚洲大胆精品| 欧美日韩一区二区三区69堂| 亚洲色图官网| 一区二区三区欧美久久| 亚洲欧洲精品在线观看| 日本天堂在线| 懂色av一区二区夜夜嗨| 国产在线视频一区| 波多野结衣视频在线看| 国产精品婷婷| 97精品免费视频| 麻豆视频在线观看| 欧美日本在线| 久久人人爽人人爽爽久久| 美女av免费看| 欧美伦理影院| 亚洲天堂男人的天堂| 久久久久国产精品区片区无码| 日韩免费成人| 欧美一区二区三区日韩视频| 久久久久xxxx| 国产亚洲欧美日韩精品一区二区三区 | 国产精品欧美日韩一区二区| 中文字字幕在线中文| 国产欧美日韩综合一区在线播放| 欧美国产日韩在线| 农村黄色一级片| 欧美日韩福利| 欧美激情视频一区二区三区不卡| 日韩一级片大全| 亚洲综合小说| 欧美激情国产精品| 日韩精品一区二区在线播放| 怡红院精品视频在线观看极品| 欧美黄色www| 精品午夜福利视频| 最新日韩在线| 欧美亚洲视频在线观看| 高清乱码免费看污| 视频一区视频二区中文| 国产精品久久久久久亚洲调教 | 动漫一区二区在线| 免费观看国产精品| 99视频精品全部免费在线| 精品蜜桃传媒| 韩国精品视频| 国产精品久久久久影院| 天堂av免费看| 欧美人与性动交α欧美精品济南到| 一区二区三区久久| 免费在线观看亚洲视频| 国产综合色区在线观看| 欧美日韩国产大片| 国产精品二区视频| 少妇一区二区三区| 色诱女教师一区二区三区| 暗呦丨小u女国产精品| 亚洲欧洲视频| 国产精品91久久| 一级黄色片在线| 丁香六月久久综合狠狠色| 久久久com| 日韩三级影院| 亚洲成人av一区| 久久精品网站视频| av一级久久| 日韩精品一二三四区| 色www亚洲国产阿娇yao| 影音先锋久久久| 国产精品久久久久久久久久| 国产三级自拍视频| 26uuu亚洲| 91免费网站视频| 欧美男男激情videos| 欧美日韩成人激情| 在线免费观看日韩av| 日韩专区精品| 国产91对白在线播放| 国产精品污视频| 久久影音资源网| 国产欧美综合一区| 欧美精品高清| 亚洲第一福利网站| 登山的目的在线| 美女诱惑黄网站一区| 91久久夜色精品国产网站| 五月天婷婷激情网| 一区二区视频在线看| 日本888xxxx| 老牛国内精品亚洲成av人片| 日韩视频免费大全中文字幕| 依依成人综合网| 国产成人精品免费| 国产精品波多野结衣| 黄色成人在线视频| 精品亚洲夜色av98在线观看| 欧美黄色免费在线观看| 老司机午夜精品| 欧美最大成人综合网| www.综合| 欧美电影精品一区二区| 日韩成人短视频| 久久精品国产99国产精品| 欧美精品欧美精品| av在线加勒比| 欧美成人精品1314www| 999精品在线视频| 秋霞电影网一区二区| 久久国产精品一区二区三区 | 欧美最顶级的aⅴ艳星| 狠狠躁日日躁夜夜躁av| 亚洲视频综合在线| 五月天av在线播放| 三区四区不卡| 国产精品偷伦免费视频观看的| 天堂v视频永久在线播放| 一区二区三区日韩精品视频| 成人免费播放视频| 一区二区在线影院| 亚洲自拍偷拍在线| 免费在线毛片网站| 91精品啪在线观看国产60岁| 国产精品免费在线视频| 麻豆精品在线播放| 亚洲天堂电影网| 亚洲国产aⅴ精品一区二区三区| 中文字幕日韩在线观看| 在线观看av大片| 亚洲欧洲日韩av| 国产性生活一级片| 欧美日韩一区二区国产| 超碰97国产在线| 欧美野外wwwxxx| 亚洲国产精品久久久| 久久99精品波多结衣一区| 久久综合av免费| 丝袜制服一区二区三区| 四季av一区二区三区免费观看| 国产一区二区视频在线观看| 18+激情视频在线| 精品福利一区二区三区免费视频| 久久精品国产亚洲AV无码麻豆| 不卡的av在线| 免费无码av片在线观看| 成人无号精品一区二区三区| 国产精品久久久久久av| 欧美精品hd| 日韩免费一区二区| 国产小视频在线免费观看| 国产亚洲美州欧州综合国| jizz欧美性11| 欧美精品午夜| 蜜桃成人在线| 99久久这里有精品| 久久久久久久一| 国产专区在线播放| 正在播放一区二区| 国产成人在线免费观看视频| 国产日本欧洲亚洲| 欧美在线a视频| 先锋影音国产一区| 五月天色婷婷综合| 加勒比视频一区| 国产精品美女在线| 欧美理论电影| 中文字幕九色91在线| 午夜免费福利视频| 在线观看亚洲成人| 久久中文字幕无码| 日本一区二区免费在线| 野战少妇38p| 美女视频网站久久| 国产免费黄视频| 99久久精品国产亚洲精品 | 国产成人ay| 99高清视频有精品视频| 台湾佬中文娱乐久久久| 欧美国产在线电影| 欧美a在线看| 亚洲免费影视第一页| 亚洲伦理在线观看| 欧美日韩国产美女| 800av免费在线观看| 一区二区三区欧美激情| 国产精品久久久久久成人| 成人免费视频免费观看| 一级黄色大片儿| 免费一级片91| 日韩黄色片视频| 国色天香一区二区| 不卡中文字幕在线| 精品国产一区二区三区| 精品伦理一区二区三区| www国产精品| 亚洲伊人第一页| 日韩成人在线一区| 国产999精品视频| 日韩电影毛片| 久久免费视频网| 先锋成人av| 日韩视频精品在线| 一区二区三区视频在线观看视频| 亚洲欧洲日本专区| 天堂91在线| 亚洲第一区第二区| 亚洲国产精品久久人人爱潘金莲 | 国产精品久久一级| 亚洲AV无码国产成人久久| 暴力调教一区二区三区| 欧美熟妇精品一区二区| 国产揄拍国内精品对白| 国模私拍视频在线观看| 免费高清成人在线| 午夜久久久精品| 男女激情视频一区| 天天爽天天爽夜夜爽| 日韩电影在线免费观看| 成人一区二区三| 丝袜美腿亚洲综合| 不卡影院一区二区| 日韩av中文在线观看| 国产三级三级三级看三级| 三级久久三级久久久| 蜜臀久久99精品久久久酒店新书| 国产农村妇女毛片精品久久莱园子| 国产美女作爱全过程免费视频| 亚洲欧美一区在线| www.国产在线播放| 亚洲美女黄网| 国产精品久久中文字幕| 午夜亚洲精品| 日本熟妇人妻中出| 精油按摩中文字幕久久| gai在线观看免费高清| 国产精品资源在线观看| 最新中文字幕日本| 91日韩在线专区| 一色道久久88加勒比一| 国产精品欧美久久久久无广告| 国产亚洲精品久久久久久豆腐| 亚洲人成网站色在线观看| 欧美日韩中文字幕在线观看| 亚洲国产精品久久人人爱蜜臀| 日本熟妇一区二区| 在线免费亚洲电影| 国产一区二区在线视频观看| 日韩一区二区三区免费看| 欧美熟妇乱码在线一区| 亚洲免费伊人电影在线观看av| a视频网址在线观看| 精品自在线视频| 久热在线观看视频| 国产精品专区h在线观看| 视频在线观看免费影院欧美meiju| 国产在线精品一区二区中文 | 国产一区在线不卡| 老司机午夜免费福利| 久久精品免费在线观看| 91嫩草丨国产丨精品| 亚洲动漫第一页| 亚洲欧美日韩一区二区三区四区| 91精品中文字幕一区二区三区| 好男人www在线视频| 中文字幕亚洲欧美| 毛片在线导航| 国产精品夜色7777狼人| 国产三级精品三级在线观看国产| 欧美三日本三级少妇三99| 欧美在线高清| 日韩无套无码精品| 成人午夜免费av| 女人裸体性做爰全过| 性做久久久久久免费观看| 一级黄色免费片| 亚洲欧洲第一视频| av在线私库| 91精品视频在线播放| 国产欧美日韩精品一区二区三区| 正在播放精油久久| 免费在线成人| 无码人妻aⅴ一区二区三区玉蒲团| 国产午夜精品福利| 国偷自拍第113页| 日韩网站在线看片你懂的| www在线播放| 欧美亚洲视频在线看网址| 99tv成人影院| 婷婷久久伊人| 亚洲理伦在线| 韩国三级与黑人| 国产精品你懂的| 少妇太紧太爽又黄又硬又爽| 欧美α欧美αv大片| 免费黄网站在线| 国产精品久久久久9999| 少妇精品导航| 91专区在线观看| 国产成人精品1024| 中文字幕人妻一区二| 欧美日韩在线免费视频| 免费黄色片在线观看| 97成人精品视频在线观看| 日韩不卡在线视频| 宅男一区二区三区| 毛片av中文字幕一区二区| 极品蜜桃臀肥臀-x88av| 色综合色狠狠综合色| 欧美自拍第一页| 欧美激情小视频| 136国产福利精品导航网址应用| 中文字幕一区二区三区精彩视频 | 国产精品6699| 九九视频精品全部免费播放| 给我免费播放片在线观看| 风间由美性色一区二区三区| 极品盗摄国产盗摄合集| 日韩亚洲欧美一区二区三区| а√天堂官网中文在线| 999日本视频| 国产精品99免费看| 国产精品入口麻豆| 调教+趴+乳夹+国产+精品| 爽爽视频在线观看| 国产91网红主播在线观看| 国产亚洲一区二区三区不卡| 欧美两根一起进3p做受视频| 日本一区二区三区视频视频| 中文字字幕在线观看| 深夜福利91大全| 粉嫩av国产一区二区三区| 777久久精品一区二区三区无码| 国产美女精品在线| 国产在线观看你懂的| 亚洲精品久久久久中文字幕欢迎你 | 一级久久久久久| 中文字幕在线观看日韩| 国产精品视频一区二区三区| 隔壁人妻偷人bd中字| www.成人网.com| 久久精品无码av| 中文字幕少妇一区二区三区| 国产精品国产三级在线观看| 国产精品视频一二三四区| 99久久精品免费看国产| 久久中文字幕免费| 久久激情视频免费观看| eeuss国产一区二区三区四区| av免费观看网| 中文字幕中文在线不卡住| 亚洲高清在线观看视频| 欧洲亚洲免费在线| 欧美r级电影| 日批在线观看视频| 欧洲一区二区av| aa在线视频| 欧美久久久久久久| 激情综合网天天干| 西西44rtwww国产精品| 少妇av一区二区三区| 99久久免费精品国产72精品九九| 777久久久精品一区二区三区| 国产精品久久久久久久久免费樱桃| 精品人妻少妇AV无码专区 | 日本美女久久| 妺妺窝人体色www看人体| 久久精品亚洲精品国产欧美| 国产喷水吹潮视频www| 欧美有码在线观看视频|