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

盤點(diǎn)總結(jié)2023年Go并發(fā)庫有哪些變化

 更新時(shí)間:2023年12月20日 09:41:59   作者:晁岳攀(鳥窩)?鳥窩聊技術(shù)  
這篇文章主要為大家介紹了2023年Go并發(fā)庫的變化盤點(diǎn)總結(jié),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

2023 年來, Go 的并發(fā)庫又有了一些變化,這篇文章是對(duì)這些變化的綜述。小細(xì)節(jié)的變化,比如 typo、文檔變化等無關(guān)大局的變化就不介紹了。

sync.Once

Go 1.21.0 中增加了和 Once 相關(guān)的三個(gè)函數(shù),便于 Once 的使用。

func OnceFunc(f func()) func()
func OnceValue[T any](f func( "T any") T) func() T
func OnceValues[T1, T2 any](f func( "T1, T2 any") (T1, T2)) func() (T1, T2)

這三個(gè)函數(shù)的功能分別是:

  • OnceFunc:返回一個(gè)函數(shù)g,多次調(diào)用這個(gè)函數(shù)g,只會(huì)執(zhí)行一次f。如果f執(zhí)行時(shí) panic, 則后續(xù)調(diào)用這個(gè)函數(shù)g不會(huì)再執(zhí)行f,但是每次調(diào)用都會(huì) panic。

  • OnceValue:返回一個(gè)函數(shù)g,多次調(diào)用這個(gè)函數(shù)g,只會(huì)執(zhí)行一次f,函數(shù)g返回值類型是 T。比上一個(gè)g多了一個(gè)返回值。panic 原理同上。

  • OnceValues:返回一個(gè)函數(shù)g,多次調(diào)用這個(gè)函數(shù)g,只會(huì)執(zhí)行一次f,函數(shù)g返回值類型是(T1, T2)。比上一個(gè)g又多了一個(gè)返回值。panic 原理同上。

當(dāng)然理論上你還可以增加更多的函數(shù),返回更多的返回值,因?yàn)?Go 沒有 Tuple 類型,所以這里還不能簡化函數(shù)g的返回值為 Tuple 類型。反正 Go 1.21.0 就只增加了這三個(gè)函數(shù)。

這個(gè)有什么好處呢?先前我們使用sync.Once的時(shí)候,比如初始化一個(gè)線程池,我們需要定義一個(gè)線程池的變量,每次訪問線程池變量的時(shí)候,我需要調(diào)用一下sync.Once.Do:

func TestOnce(t *testing.T) {
 var pool any
 var once sync.Once
 var initFn = func() {
  // init pool
  pool = 1
 }

 for i := 0; i < 10; i++ {
  once.Do(initFn)
  t.Log(pool)
 }
}

如果使用OnceValue,就可以簡化代碼:

func TestOnceValue(t *testing.T) {
 var initPool = func() any {
  return 1
 }
 var poolGenerator = sync.OnceValue(initPool)

 for i := 0; i < 10; i++ {
  t.Log(poolGenerator())
 }
}

代碼略微簡化,獲取單例的時(shí)候只需調(diào)用返回的函數(shù)g即可。

所以基本上,這三個(gè)函數(shù)只是對(duì) sync.Once 做了封裝,更方便使用。

理解 copyChecker

我們知道, sync.Cond有兩個(gè)字段noCopycheckernoCopy通過go vet工具能夠靜態(tài)編譯時(shí)檢查出來,但是checker是在運(yùn)行時(shí)檢查的:

type Cond struct {
 noCopy noCopy
 // L is held while observing or changing the condition
 L Locker
 notify  notifyList
 checker copyChecker
}

先前copyChecker的判斷條件如下,雖然簡單的三行,但是不容易理解:

func (c *copyChecker) check() {
 if uintptr(*c) != uintptr(unsafe.Pointer(c)) &&
  !atomic.CompareAndSwapUintptr((*uintptr)(c), 0, uintptr(unsafe.Pointer(c))) &&
  uintptr(*c) != uintptr(unsafe.Pointer(c)) {
  panic("sync.Cond is copied")
 }
}

現(xiàn)在加上了注釋,解釋了這三行的意義:

func (c *copyChecker) check() {
 // Check if c has been copied in three steps:
 // 1. The first comparison is the fast-path. If c has been initialized and not copied, this will return immediately. Otherwise, c is either not initialized, or has been copied.
 // 2. Ensure c is initialized. If the CAS succeeds, we're done. If it fails, c was either initialized concurrently and we simply lost the race, or c has been copied.
 // 3. Do step 1 again. Now that c is definitely initialized, if this fails, c was copied.
 if uintptr(*c) != uintptr(unsafe.Pointer(c)) &&
  !atomic.CompareAndSwapUintptr((*uintptr)(c), 0, uintptr(unsafe.Pointer(c))) &&
  uintptr(*c) != uintptr(unsafe.Pointer(c)) {
  panic("sync.Cond is copied")
 }
}

主要邏輯

在以下 3 步:

  • 第一步是一個(gè)快速檢查,直接比較 c 指針和 c 本身的指針,如果不相等則表示已被復(fù)制。這是最快的檢查路徑。

  • 第二步確保 c 已經(jīng)被初始化。使用 CAS (CompareAndSwap)來初始化。如果 CAS 失敗,說明c 已經(jīng)在其他 goroutine 初始化,或者被復(fù)制了。

  • 第三步再次執(zhí)行第一步的檢查。因?yàn)檫@時(shí)我們清楚的知道 c 已經(jīng)初始化了,所以如果檢查失敗,就可以確認(rèn) c 被復(fù)制了。

整個(gè)邏輯就是使用 CAS 配合兩次指針檢查,來確保判斷的正確性。

總的來說,第一步快速檢查是性能優(yōu)化。第二步使用 CAS 確保初始化。第三步再次檢查來確保判斷。

sync.Map 的一處優(yōu)化

先前, sync.Map 的 Range 函數(shù)的實(shí)現(xiàn)如下:

func (m *Map) Range(f func(key, value any) bool) {
    ...
    if read.amended {
   read = readOnly{m: m.dirty}
   m.read.Store(&read)
   m.dirty = nil
   m.misses = 0
 }
    ...
}

其中有一段代碼:m.read.Store(&read),會(huì)導(dǎo)致read逃逸到堆上,通過下面的一個(gè)小技巧,避免了read的逃逸(通過一個(gè)新的變量):

func (m *Map) Range(f func(key, value any) bool) {
    ...
 if read.amended {
  read = readOnly{m: m.dirty}
  copyRead := read
  m.read.Store(&copyRead)
  m.dirty = nil
  m.misses = 0
 }
    ...
}

issue #62404[1]對(duì)這個(gè)問題進(jìn)行了分析。

sync.Once 的實(shí)現(xiàn)中 done 使用 atomic.Uint32 替換

先前sync.Once的實(shí)現(xiàn)如下:

type Once struct {
 done uint32
 m    Mutex
}

其中字段done是一個(gè)uint32類型,用來表示Once是否已經(jīng)執(zhí)行過了。這個(gè)字段的類型是uint32,而不是bool,是因?yàn)?code>uint32類型可以使用atomic包的原子操作,而bool類型不能。

現(xiàn)在sync.Once的實(shí)現(xiàn)如下:

type Once struct {
 done atomic.Uint32
 m    Mutex
}

自從 go 1.19 提供了對(duì)基本類型的原子封裝,Go 標(biāo)準(zhǔn)庫大量代碼都被atomic.XXX類型鎖替換。

我個(gè)人認(rèn)為,目前這個(gè)修改相對(duì)于先前的實(shí)現(xiàn),性能上在某些情況下可能會(huì)有性能的下降,我會(huì)專門寫一篇文章進(jìn)行探討。

除了sync.Once,還有一批類型使用了atomic.XXX類型替換原來的使用方法,有必要可以進(jìn)行替換么?

sync.OnceFunc 初始實(shí)現(xiàn)的優(yōu)化

初始的sync.OnceFunc的實(shí)現(xiàn)如下:

func OnceFunc(f func()) func() {
 var (
  once  Once
  valid bool
  p     any
 )
 g := func() {
  defer func() {
   p = recover()
   if !valid {
    panic(p)
   }
  }()
  f()
  valid = true
 }
 return func() {
  once.Do(g)
  if !valid {
   panic(p)
  }
 }
}

仔細(xì)看這段代碼,你會(huì)發(fā)現(xiàn),傳遞給OnceFunc/OnceValue/OnceValues的函數(shù)f,即使執(zhí)行完一次,只要返回的g函數(shù)好活著沒有被垃圾回收,這個(gè)f就一直存活。這是沒必要的,因?yàn)?code>f只需要執(zhí)行一次,執(zhí)行完就可以被垃圾回收了。所以,這里可以對(duì)f進(jìn)行一次優(yōu)化,讓f執(zhí)行完就設(shè)置為nil,這樣就可以被垃圾回收了。

func OnceFunc(f func()) func() {
 var (
  once  Once
  valid bool
  p     any
 )
 // Construct the inner closure just once to reduce costs on the fast path.
 g := func() {
  defer func() {
   p = recover()
   if !valid {
    // Re-panic immediately so on the first call the user gets a
    // complete stack trace into f.
    panic(p)
   }
  }()
  f()
  f = nil      // Do not keep f alive after invoking it.
  valid = true // Set only if f does not panic.
 }
 return func() {
  once.Do(g)
  if !valid {
   panic(p)
  }
 }
}

context

我們知道,在 Go 1.20 中, 新增加了一個(gè)WithCancelCause方法(func WithCancelCause(parent Context) (ctx Context, cancel CancelCauseFunc)),我們?cè)?code>cancel的時(shí)候可以把 cancel 的原因傳遞給WithCancelCause產(chǎn)生的 Context,這樣可以通過context.Cause方法獲取到cancel的原因。

ctx, cancel := context.WithCancelCause(parent)
cancel(myError)
ctx.Err() // 返回 context.Canceled
context.Cause(ctx) // 返回 myError

當(dāng)然這個(gè)實(shí)現(xiàn)只進(jìn)行了一半,因?yàn)槌瑫r(shí)相關(guān)的 Context 也需要增加這個(gè)功能,所以在 Go 1.21.0 中又新增了兩個(gè)相關(guān)的函數(shù):

func WithDeadlineCause(parent Context, d time.Time, cause error) (Context, CancelFunc)
func WithTimeoutCause(parent Context, timeout time.Duration, cause error) (Context, CancelFunc)

這兩個(gè)和WithCancelCause還不太一樣,不是利用返回的 cancel 函數(shù)傳遞原因,而是直接在函數(shù)參數(shù)中傳遞原因。

Go 1.21.0 還增加了一個(gè)AfterFunc函數(shù),這個(gè)函數(shù)和time.AfterFunc類似,但是返回的是一個(gè)Context,這個(gè)Context在超時(shí)后會(huì)自動(dòng)取消,這個(gè)函數(shù)的實(shí)現(xiàn)如下:

func AfterFunc(ctx Context, f func()) (stop func() bool)

指定的Context在在 done(超時(shí)或者取消),如果 context 已經(jīng) done,那么f立即被調(diào)用。返回的stop函數(shù)用來停止f的調(diào)用,如果stop被調(diào)用并且返回 true,f不會(huì)被調(diào)用。

這是一個(gè)輔助函數(shù),但是難以理解,估計(jì)這個(gè)函數(shù)不會(huì)被廣泛的使用。

其他一些小性能的優(yōu)化比如type emptyCtx int替換成type emptyCtx struct{}等等就不用提了。

增加了一個(gè)func WithoutCancel(parent Context) Context, 當(dāng) parent 被取消時(shí),不會(huì)波及到這個(gè)函數(shù)返回的 Context。

Coroutines for Go

在今年 7 月,Russ Coxx 寫了一篇巨論:Coroutines for Go[2]。

個(gè)人不看好在 Go 標(biāo)準(zhǔn)庫實(shí)現(xiàn)這個(gè)東西,我感覺 Rob Pike 也不會(huì)同意,但是這個(gè)東西社區(qū)如果去實(shí)現(xiàn)一個(gè)庫,我覺得還是有可能的,返回如果大家不看好,社區(qū)的庫自然會(huì)消亡。

否則,漸漸的 Go 迷失了它的初心: 簡單好用。

社區(qū)的一些協(xié)程庫:

  • coroutine[3]

  • routine[4]

  • gocoro[5]

你在 go.dev 還能搜到一些,這里就不贅述了。

golang.org/x/sync 沒有明顯改動(dòng)

errgroup支持使用withCancelCause設(shè)置 cause。singleflight的 panicError 增加 Unwrap 方法。

參考資料

[1]

issue #62404: https://github.com/golang/go/issues/62404

[2]

Coroutines for Go: https://research.swtch.com/coro

[3]

coroutine: https://github.com/stealthrocket/coroutine

[4]

routine: https://github.com/solarlune/routine

[5]

gocoro: https://github.com/SolarLune/gocoro

以上就是盤點(diǎn)總結(jié)2023年Go并發(fā)庫有哪些變化的詳細(xì)內(nèi)容,更多關(guān)于Go 并發(fā)庫變化的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Golang 如何限制木馬圖片上傳服務(wù)器的實(shí)例

    Golang 如何限制木馬圖片上傳服務(wù)器的實(shí)例

    本文主要介紹了Golang 如何限制木馬圖片上傳服務(wù)器的實(shí)例,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • golang中的defer函數(shù)理解

    golang中的defer函數(shù)理解

    defer是Go語言中的延遲執(zhí)行語句,用來添加函數(shù)結(jié)束時(shí)執(zhí)行的代碼,常用于釋放某些已分配的資源、關(guān)閉數(shù)據(jù)庫連接、斷開socket連接、解鎖一個(gè)加鎖的資源,這篇文章主要介紹了golang中的defer函數(shù)理解,需要的朋友可以參考下
    2022-10-10
  • Golang中ringbuffer的實(shí)現(xiàn)與應(yīng)用場(chǎng)景詳解

    Golang中ringbuffer的實(shí)現(xiàn)與應(yīng)用場(chǎng)景詳解

    ringbuffer因?yàn)樗軓?fù)用緩沖空間,通常用于網(wǎng)絡(luò)通信連接的讀寫,雖然市面上已經(jīng)有了go寫的諸多版本的ringbuffer組件,但還是自己造一個(gè)吧
    2023-06-06
  • 十個(gè)Golang開發(fā)中應(yīng)該避免的錯(cuò)誤總結(jié)

    十個(gè)Golang開發(fā)中應(yīng)該避免的錯(cuò)誤總結(jié)

    Go是一種靜態(tài)類型的、并發(fā)的、垃圾收集的編程語言,由谷歌開發(fā)。開發(fā)人員在編寫Go代碼時(shí)總會(huì)有一些常見的錯(cuò)誤,下面是Go語言中需要避免的十大壞錯(cuò)誤,希望對(duì)大家有所幫助
    2023-03-03
  • 詳解Golang?ProtoBuf的基本語法總結(jié)

    詳解Golang?ProtoBuf的基本語法總結(jié)

    最近項(xiàng)目是采用微服務(wù)架構(gòu)開發(fā)的,各服務(wù)之間通過gPRC調(diào)用,基于ProtoBuf序列化協(xié)議進(jìn)行數(shù)據(jù)通信,因此接觸學(xué)習(xí)了Protobuf,本文會(huì)對(duì)Protobuf的語法做下總結(jié),感興趣的小伙伴們可以參考借鑒,希望對(duì)大家能有所幫助
    2022-10-10
  • Go語言連接Oracle數(shù)據(jù)庫的方法

    Go語言連接Oracle數(shù)據(jù)庫的方法

    這篇文章主要介紹了Go語言連接Oracle數(shù)據(jù)庫的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-02-02
  • Go語言并發(fā)之Select多路選擇操作符用法詳解

    Go語言并發(fā)之Select多路選擇操作符用法詳解

    Go?語言借用多路復(fù)用的概念,提供了?select?關(guān)鍵字,用于多路監(jiān)聽多個(gè)通道,本文就來和大家聊聊Go語言中Select多路選擇操作符的具體用法,希望對(duì)大家有所幫助
    2023-06-06
  • 一文搞懂Go語言操作Redis的方法

    一文搞懂Go語言操作Redis的方法

    Redis是一個(gè)開源的內(nèi)存數(shù)據(jù)庫,在項(xiàng)目開發(fā)中redis的使用也比較頻繁,本文介紹了Go語言中g(shù)o-redis庫的基本使用。感興趣的小伙伴們可以參考借鑒一下
    2022-09-09
  • go語言fasthttp使用實(shí)例小結(jié)

    go語言fasthttp使用實(shí)例小結(jié)

    fasthttp?是一個(gè)使用?Go?語言開發(fā)的?HTTP?包,主打高性能,針對(duì)?HTTP?請(qǐng)求響應(yīng)流程中的?hot?path?代碼進(jìn)行了優(yōu)化,下面我們就來介紹go語言fasthttp使用實(shí)例小結(jié),感興趣的朋友跟隨小編一起看看吧
    2024-03-03
  • Golang語言如何避免空指針引發(fā)的panic詳解

    Golang語言如何避免空指針引發(fā)的panic詳解

    簡單地說go語言的指針類型和C/C++的指針類型用法是一樣的,除了出去安全性的考慮,go語言增加了一些限制,這篇文章主要給大家介紹了關(guān)于Golang語言如何避免空指針引發(fā)panic的相關(guān)資料,需要的朋友可以參考下
    2022-01-01

最新評(píng)論

田阳县| 平湖市| 中方县| 宿州市| 微博| 万山特区| 仁化县| 辉南县| 从化市| 阳东县| 达日县| 中牟县| 昌乐县| 长岛县| 双鸭山市| 呼伦贝尔市| 怀集县| 于都县| 凤城市| 麻阳| 山东| 绥宁县| 孟津县| 石林| 自治县| 白河县| 新营市| 邢台县| 龙川县| 汾西县| 蕲春县| 曲沃县| 吉隆县| 建始县| 峨山| 东乌珠穆沁旗| 关岭| 梅河口市| 达日县| 丰台区| 大丰市|