最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Go實(shí)現(xiàn)并發(fā)緩存的示例代碼

 更新時(shí)間:2023年10月07日 09:58:35   作者:ethannotlazy  
高并發(fā)數(shù)據(jù)存儲(chǔ)是現(xiàn)代互聯(lián)網(wǎng)應(yīng)用開(kāi)發(fā)中常遇到的一大挑戰(zhàn),本文主要介紹了Go實(shí)現(xiàn)并發(fā)緩存的示例代碼,具有一定的參考價(jià)值,感興趣的可以了解一下

并發(fā)不安全的 Memo

首先用一個(gè)例子演示函數(shù)記憶

// A Memo caches the results of calling a Func.
type Memo struct {
	f     Func
	cache map[string]result
}

// Func is the type of the function to memoize.
type Func func(key string) (interface{}, error)

type result struct {
	value interface{}
	err   error
}

func New(f Func) *Memo {
	return &Memo{f: f, cache: make(map[string]result)}
}

// NOTE: not concurrency-safe!
func (memo *Memo) Get(key string) (interface{}, error) {
	res, ok := memo.cache[key]
	if !ok {
		res.value, res.err = memo.f(key)
		memo.cache[key] = res
	}
	return res.value, res.err
}

其中函數(shù)f是一個(gè)重量級(jí)的計(jì)算函數(shù),調(diào)用它的代價(jià)很大,所以要將結(jié)果緩存到一個(gè)map中加快每次調(diào)用。這就是函數(shù)記憶。
每次調(diào)用Get,將從memo里查詢(xún)結(jié)果,如果沒(méi)查到,就要調(diào)用函數(shù)f計(jì)算結(jié)果,再將它記錄到緩存中。
以上實(shí)現(xiàn)Get方法在沒(méi)有使用同步的情況下更新了緩存cache,整個(gè)Get函數(shù)不是并發(fā)安全的

安全但偽并發(fā)的 Memo

考慮每次調(diào)用Get方法都加鎖:

type Memo struct {
	f     Func
	mu    sync.Mutex // guards cache
	cache map[string]result
}

// Get is concurrency-safe.
func (memo *Memo) Get(key string) (value interface{}, err error) {
	memo.mu.Lock()
	res, ok := memo.cache[key]
	if !ok {
		res.value, res.err = memo.f(key)
		memo.cache[key] = res
	}
	memo.mu.Unlock()
	return res.value, res.err
}

由于每次調(diào)用都請(qǐng)求互斥鎖,Get又將并行的請(qǐng)求操作串行化了。

會(huì)導(dǎo)致多余計(jì)算的 Memo

考慮以下改進(jìn):

func (memo *Memo) Get(key string) (value interface{}, err error) {
	memo.mu.Lock()
	res, ok := memo.cache[key]
	memo.mu.Unlock()
	if !ok {
		res.value, res.err = memo.f(key)

		// Between the two critical sections, several goroutines
		// may race to compute f(key) and update the map.
		memo.mu.Lock()
		memo.cache[key] = res
		memo.mu.Unlock()
	}
	return res.value, res.err
}

該版本分兩次獲取鎖:第一次用于查詢(xún)緩存,第二次用于在查詢(xún)無(wú)結(jié)果時(shí)進(jìn)行更新。
在理想情況下,我們應(yīng)該避免這種額外的處理。這個(gè)功能有時(shí)被稱(chēng)為重復(fù)抑制(duplication suppression)。

通過(guò)通道進(jìn)行重復(fù)抑制

在第四個(gè)版本的緩存中,我們?yōu)槊總€(gè)entry新加了一個(gè)通道ready。在設(shè)置完entryresult字段后,通道會(huì)關(guān)閉,正在等待的goroutine會(huì)收到廣播,就可以從entry中讀取結(jié)果了。

// Func is the type of the function to memoize.
type Func func(string) (interface{}, error)

type result struct {
	value interface{}
	err   error
}

type entry struct {
	res   result
	ready chan struct{} // closed when res is ready
}

func New(f Func) *Memo {
	return &Memo{f: f, cache: make(map[string]*entry)}
}

type Memo struct {
	f     Func
	mu    sync.Mutex // guards cache
	cache map[string]*entry	//現(xiàn)在緩存返回的是一個(gè)entry
}

func (memo *Memo) Get(key string) (value interface{}, err error) {
	memo.mu.Lock()
	e := memo.cache[key]
	if e == nil {
		// This is the first request for this key.
		// This goroutine becomes responsible for computing
		// the value and broadcasting the ready condition.
		e = &entry{ready: make(chan struct{})}
		memo.cache[key] = e
		memo.mu.Unlock()

		e.res.value, e.res.err = memo.f(key)

		close(e.ready) // broadcast ready condition
	} else {
		// This is a repeat request for this key.
		memo.mu.Unlock()

		<-e.ready // wait for ready condition
	}
	return e.res.value, e.res.err
}

當(dāng)Get函數(shù)發(fā)現(xiàn)緩存memo中沒(méi)有記錄時(shí),它構(gòu)造一個(gè)entry放到緩存中,但這時(shí)key對(duì)應(yīng)的結(jié)果還未計(jì)算。
這時(shí),如果其他goroutine調(diào)用了Get函數(shù)查詢(xún)同樣的key時(shí),它會(huì)到達(dá)<-e.ready語(yǔ)句并因等待通道數(shù)據(jù)而阻塞。只有當(dāng)計(jì)算結(jié)束,負(fù)責(zé)計(jì)算結(jié)果的goroutine將通道關(guān)閉后,其它goroutine才能夠得以繼續(xù)執(zhí)行,并查詢(xún)出結(jié)果。

  • 當(dāng)一個(gè)goroutine試圖查詢(xún)一個(gè)不存在的結(jié)果時(shí),它創(chuàng)建一個(gè)entry放到緩存中,并解鎖,然后調(diào)用f進(jìn)行計(jì)算。計(jì)算完成后更新相應(yīng)的entry就可以將ready通道關(guān)閉;
  • 當(dāng)一個(gè)goroutine試圖查詢(xún)一個(gè)已經(jīng)存在的結(jié)果時(shí),他應(yīng)該立即放棄鎖,并等待查到的entry的通道的關(guān)閉。

「通過(guò)通信共享內(nèi)存」的另一設(shè)計(jì)

以上介紹了共享變量并上鎖的方法,另一種方案是通信順序進(jìn)程。
在新的設(shè)計(jì)中,map變量限制在一個(gè)監(jiān)控goroutine中,而Get的調(diào)用者則改為發(fā)送消息

// Func is the type of the function to memoize.
type Func func(key string) (interface{}, error)

// A result is the result of calling a Func.
type result struct {
	value interface{}
	err   error
}

type entry struct {
	res   result
	ready chan struct{} // closed when res is ready
}

// A request is a message requesting that the Func be applied to key.
type request struct {
	key      string
	response chan&lt;- result // the client wants a single result
}

type Memo struct{ requests chan request }

// New returns a memoization of f.  Clients must subsequently call Close.
func New(f Func) *Memo {
	memo := &amp;Memo{requests: make(chan request)}
	go memo.server(f)
	return memo
}

func (memo *Memo) Get(key string) (interface{}, error) {
	response := make(chan result)
	memo.requests &lt;- request{key, response}
	res := &lt;-response
	return res.value, res.err
}

func (memo *Memo) Close() { close(memo.requests) }

//!-get

//!+monitor

func (memo *Memo) server(f Func) {
	cache := make(map[string]*entry)
	for req := range memo.requests {
		e := cache[req.key]
		if e == nil {
			// This is the first request for this key.
			e = &amp;entry{ready: make(chan struct{})}
			cache[req.key] = e
			go e.call(f, req.key) // call f(key)
		}
		go e.deliver(req.response)
	}
}

func (e *entry) call(f Func, key string) {
	// Evaluate the function.
	e.res.value, e.res.err = f(key)
	// Broadcast the ready condition.
	close(e.ready)
}

func (e *entry) deliver(response chan&lt;- result) {
	// Wait for the ready condition.
	&lt;-e.ready
	// Send the result to the client.
	response &lt;- e.res
}

Get方法創(chuàng)建一個(gè)response通道,并將它放在一個(gè)請(qǐng)求里,然后把它發(fā)送給監(jiān)控goroutine,然后從自己創(chuàng)建的response通道中讀取。

監(jiān)控goroutine(即server方法)不斷從request通道中讀取,直至該通道被關(guān)閉。對(duì)于每個(gè)請(qǐng)求,它先從緩存中查詢(xún),如果沒(méi)找到則創(chuàng)建并插入一個(gè)新的entry
監(jiān)控goroutine先創(chuàng)建一個(gè)entry放到緩存中,然后它調(diào)用go e.call(f, req.key)創(chuàng)建一個(gè)gorouitne來(lái)計(jì)算結(jié)果、關(guān)閉ready通道。與此同時(shí)它調(diào)用go e.deliver(req.response)等待ready通道關(guān)閉,并將結(jié)果發(fā)送到response通道中;

如果監(jiān)控goroutine直接從緩存找到了結(jié)果,那么根據(jù)key查到的entry已經(jīng)包含一個(gè)已經(jīng)關(guān)閉的通道,它調(diào)用go e.deliver(req.response)就可以直接將結(jié)果放到response通道中。

總結(jié)起來(lái),server負(fù)責(zé)了從請(qǐng)求通道中讀取請(qǐng)求,對(duì)于未完成計(jì)算的key,它創(chuàng)建新的goroutine執(zhí)行計(jì)算任務(wù),隨后通過(guò)請(qǐng)求中附帶的resp通道答復(fù)請(qǐng)求。

更進(jìn)一步的改造,可以限制進(jìn)行計(jì)算的goroutine數(shù)量、通過(guò)context包控制server的生命周期等。

到此這篇關(guān)于Go實(shí)現(xiàn)并發(fā)緩存的示例代碼的文章就介紹到這了,更多相關(guān)Go 并發(fā)緩存內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go語(yǔ)言正則表達(dá)式的使用詳解

    Go語(yǔ)言正則表達(dá)式的使用詳解

    正則表達(dá)式是一種進(jìn)行模式匹配和文本操縱的功能強(qiáng)大的工具。這篇文章主要介紹了Go正則表達(dá)式使用,本文給大家介紹的非常詳細(xì),對(duì)大家的工作或?qū)W習(xí)具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-03-03
  • gin框架中使用JWT的定義需求及解析

    gin框架中使用JWT的定義需求及解析

    這篇文章主要為介紹了gin框架中使用JWT的定義需求及解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪
    2022-04-04
  • go開(kāi)源項(xiàng)目用戶(hù)名密碼驗(yàn)證的邏輯鬼才寫(xiě)法

    go開(kāi)源項(xiàng)目用戶(hù)名密碼驗(yàn)證的邏輯鬼才寫(xiě)法

    這篇文章主要為大家介紹了go開(kāi)源項(xiàng)目中發(fā)現(xiàn)的一個(gè)邏輯鬼才寫(xiě)法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • Go快速開(kāi)發(fā)一個(gè)RESTful API服務(wù)

    Go快速開(kāi)發(fā)一個(gè)RESTful API服務(wù)

    這篇文章主要為大家介紹了Go快速開(kāi)發(fā)一個(gè)RESTful API服務(wù),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • 詳解Go channel管道的運(yùn)行原理

    詳解Go channel管道的運(yùn)行原理

    Go推薦通過(guò)通信來(lái)共享內(nèi)存,而channel就實(shí)現(xiàn)了這一理念。那channel是怎么運(yùn)行的呢?本文將帶你搞懂Go channel管道的運(yùn)行原理,感興趣的同學(xué)可以參考一下
    2023-05-05
  • golang調(diào)用dll的接口三種方式小結(jié)

    golang調(diào)用dll的接口三種方式小結(jié)

    本文介紹了在Go語(yǔ)言中使用syscall包的不同方法加載DLL文件,包括NewLazyDLL、MustLoadDLL、LoadLibrary以及它們?cè)谡{(diào)用函數(shù)和處理不同數(shù)據(jù)類(lèi)型轉(zhuǎn)換中的應(yīng)用,感興趣的可以了解一下
    2025-07-07
  • 以alpine作為基礎(chǔ)鏡像構(gòu)建Golang可執(zhí)行程序操作

    以alpine作為基礎(chǔ)鏡像構(gòu)建Golang可執(zhí)行程序操作

    這篇文章主要介紹了以alpine作為基礎(chǔ)鏡像構(gòu)建Golang可執(zhí)行程序操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-12-12
  • golang json性能分析詳解

    golang json性能分析詳解

    json格式可以算我們?nèi)粘W畛S玫男蛄谢袷街涣?,Go語(yǔ)言作為一個(gè)由Google開(kāi)發(fā),號(hào)稱(chēng)互聯(lián)網(wǎng)的C語(yǔ)言的語(yǔ)言,自然也對(duì)JSON格式支持很好。下面這篇文章主要給大家詳細(xì)分析介紹了golang json性能的相關(guān)資料,需要的朋友可以參考下。
    2018-02-02
  • Go語(yǔ)言使用select監(jiān)聽(tīng)多個(gè)channel的示例詳解

    Go語(yǔ)言使用select監(jiān)聽(tīng)多個(gè)channel的示例詳解

    本文將聚焦 Go 并發(fā)中的一個(gè)強(qiáng)力工具,select,這篇文章將通過(guò)實(shí)際案例學(xué)習(xí)如何優(yōu)雅地監(jiān)聽(tīng)多個(gè) Channel,實(shí)現(xiàn)多任務(wù)處理、超時(shí)控制和非阻塞通信等并發(fā)技巧
    2025-08-08
  • Go語(yǔ)言中次方表示的實(shí)現(xiàn)

    Go語(yǔ)言中次方表示的實(shí)現(xiàn)

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

最新評(píng)論

福泉市| 华安县| 恩施市| 平利县| 焉耆| 济阳县| 云林县| 会理县| 汽车| 双江| 永新县| 马边| 通许县| 密云县| 璧山县| 林州市| 河间市| 文昌市| 上饶县| 方山县| 白城市| 慈利县| 海兴县| 庆元县| 临湘市| 东城区| 同江市| 蛟河市| 巫溪县| 彭州市| 金堂县| 茂名市| 资兴市| 保康县| 城固县| 绥宁县| 马鞍山市| 渭南市| 黔西县| 南安市| 如东县|