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

Go切片導(dǎo)致rand.Shuffle產(chǎn)生重復(fù)數(shù)據(jù)的原因與解決方案

 更新時(shí)間:2025年02月17日 09:09:40   作者:飛川001  
在 Go 語(yǔ)言的實(shí)際開(kāi)發(fā)中,切片(slice)是一種非常靈活的數(shù)據(jù)結(jié)構(gòu),然而,由于其底層數(shù)據(jù)共享的特性,在某些情況下可能會(huì)導(dǎo)致意想不到的 Bug,本文將詳細(xì)分析 rand.Shuffle 之后,切片中的數(shù)據(jù)出現(xiàn)重復(fù)的問(wèn)題,探討其根本原因,并給出最佳解決方案,需要的朋友可以參考下

問(wèn)題描述

在一個(gè) Go 服務(wù)端 API 里,我們需要按照 curBatch 參數(shù)進(jìn)行分頁(yè),從 interestCfg 里分批選取 interestTagNum 個(gè)興趣標(biāo)簽,并在返回結(jié)果前對(duì)選中的數(shù)據(jù)進(jìn)行隨機(jī)打亂。

全部興趣標(biāo)簽示例:

{
    "InterestTags": [
        {"interestName":"Daily Sharing"},
        {"interestName":"Gaming"},
        {"interestName":"AI"},
        {"interestName":"test"},
        {"interestName":"Sports"},
        {"interestName":"Cars"},
        {"interestName":"other"}
    ]
}

現(xiàn)象回顧

當(dāng) curBatch = 0 時(shí),返回的數(shù)據(jù)是正確的:

{
    "InterestTags": [
        { "interestName": "Daily Sharing" },
        { "interestName": "Gaming" },
        { "interestName": "AI" }
    ]
}

但當(dāng) curBatch = 2 時(shí),測(cè)試環(huán)境出現(xiàn)了數(shù)據(jù)重復(fù)的問(wèn)題:(本地運(yùn)行正常)

1. 不隨機(jī)時(shí)(正確的結(jié)果):

{
    "InterestTags": [
        { "interestName": "other" },
        { "interestName": "Daily Sharing" },
        { "interestName": "Gaming" }
    ]
}

2. 隨機(jī)后(錯(cuò)誤的結(jié)果):

{
    "InterestTags": [
        { "interestName": "Gaming" },
        { "interestName": "Gaming" },
        { "interestName": "AI" }
    ]
}

問(wèn)題:

  • “Gaming” 出現(xiàn)了兩次,而 “test” 消失了!
  • 本地環(huán)境正常,但測(cè)試環(huán)境異常,導(dǎo)致調(diào)試變得困難。

問(wèn)題排查

數(shù)據(jù)的選擇和隨機(jī)操作邏輯如下:

interestTags := make([]model.InterestConfig, 0, interestConfig.InterestTagNum)

// 處理interestConfig,根據(jù)curBatch分批次處理
if len(interestConfig.InterestCfg) > 0 && interestConfig.InterestTagNum > 0 {
    interestAllTags := interestConfig.InterestCfg
    numBatches := (len(interestAllTags) + int(interestConfig.InterestTagNum) - 1) / int(interestConfig.InterestTagNum)
    startIdx := (curBatch % numBatches) * int(interestConfig.InterestTagNum)
    endIdx := startIdx + int(interestConfig.InterestTagNum)

    if endIdx > len(interestAllTags) {
        interestTags = interestAllTags[startIdx:]
        interestTags = append(interestTags, interestAllTags[:(endIdx-len(interestAllTags))]...)
    } else {
        interestTags = interestAllTags[startIdx:endIdx]
    }
}

// 隨機(jī)打亂 interestTags 順序
r := rand.New(rand.NewSource(time.Now().UnixNano()))
r.Shuffle(len(interestTags), func(i, j int) {
    interestTags[i], interestTags[j] = interestTags[j], interestTags[i]
})

關(guān)鍵點(diǎn)分析

  1. interestTags = interestAllTags[startIdx:endIdx] 直接從 interestAllTags 取出數(shù)據(jù),但切片是引用類(lèi)型,因此 interestTags 共享了 interestAllTags 的底層數(shù)組。
  2. rand.Shuffle 隨機(jī)交換 interestTags 里的元素,但 interestTags 指向 interestAllTags,可能導(dǎo)致原始數(shù)據(jù)被錯(cuò)誤修改。
  3. 本地和測(cè)試環(huán)境不一致,可能與 Go 運(yùn)行時(shí)的內(nèi)存管理機(jī)制高并發(fā)場(chǎng)景下的切片擴(kuò)容行為有關(guān)。

代碼驗(yàn)證

為了驗(yàn)證 interestTags 是否共享 interestAllTags 的底層數(shù)組,我們打印切片元素的內(nèi)存地址:

fmt.Println("Before Shuffle:")
for i, tag := range interestTags {
    fmt.Printf("[%d] %p: %s\n", i, &interestTags[i], tag.InterestName)
}

r.Shuffle(len(interestTags), func(i, j int) {
    interestTags[i], interestTags[j] = interestTags[j], interestTags[i]
})

fmt.Println("After Shuffle:")
for i, tag := range interestTags {
    fmt.Printf("[%d] %p: %s\n", i, &interestTags[i], tag.InterestName)
}

解決方案

方案 1:使用 append 進(jìn)行數(shù)據(jù)拷貝

為了避免 interestTags 共享 interestAllTags 的底層數(shù)組,我們需要顯式拷貝數(shù)據(jù):

interestTags = make([]model.InterestConfig, 0, interestConfig.InterestTagNum)
if endIdx > len(interestAllTags) {
    interestTags = append(interestTags, interestAllTags[startIdx:]...)
    interestTags = append(interestTags, interestAllTags[:(endIdx-len(interestAllTags))]...)
} else {
    interestTags = append(interestTags, interestAllTags[startIdx:endIdx]...)
}

為什么這樣做?

  • append(..., interestAllTags[startIdx:endIdx]...) 創(chuàng)建新的切片,避免 interestTags 共享 interestAllTags 的底層數(shù)據(jù)。
  • 獨(dú)立的數(shù)據(jù)拷貝 確保 rand.Shuffle 只影響 interestTags,不會(huì)破壞原始 interestAllTags

總結(jié)

1. 問(wèn)題原因

  • Go 切片是引用類(lèi)型,直接賦值 interestTags = interestAllTags[startIdx:endIdx] 不會(huì)創(chuàng)建新數(shù)據(jù),而是共享底層數(shù)組。
  • rand.Shuffle 可能影響 interestAllTags,導(dǎo)致元素重復(fù)。
  • 本地環(huán)境正常,但測(cè)試環(huán)境異常,可能與 Go 內(nèi)存管理切片擴(kuò)容策略有關(guān)。

2. 解決方案

  • 使用 append 進(jìn)行數(shù)據(jù)拷貝,確保 interestTags 是獨(dú)立的數(shù)據(jù),避免 rand.Shuffle 影響原始 interestAllTags。

經(jīng)驗(yàn)總結(jié)

  1. Go 切片是引用類(lèi)型,不能直接賦值,否則可能共享底層數(shù)據(jù)。
  2. 使用 rand.Shuffle 之前,必須確保數(shù)據(jù)是獨(dú)立的副本
  3. 盡量使用 append 創(chuàng)建新的切片,避免底層數(shù)組共享問(wèn)題。
  4. 不同環(huán)境表現(xiàn)不一致時(shí),應(yīng)檢查內(nèi)存管理、并發(fā)情況及數(shù)據(jù)結(jié)構(gòu)副作用。

以上就是Go切片導(dǎo)致rand.Shuffle產(chǎn)生重復(fù)數(shù)據(jù)的原因與解決方案的詳細(xì)內(nèi)容,更多關(guān)于Go rand.Shuffle產(chǎn)生重復(fù)數(shù)據(jù)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Go設(shè)計(jì)模式之原型模式圖文詳解

    Go設(shè)計(jì)模式之原型模式圖文詳解

    原型模式是一種創(chuàng)建型設(shè)計(jì)模式, 使你能夠復(fù)制已有對(duì)象, 而又無(wú)需使代碼依賴(lài)它們所屬的類(lèi),本文將通過(guò)圖片和文字讓大家可以詳細(xì)的了解Go的原型模式,感興趣的通過(guò)跟著小編一起來(lái)看看吧
    2023-07-07
  • go實(shí)現(xiàn)限流功能示例

    go實(shí)現(xiàn)限流功能示例

    這篇文章主要為大家介紹了go實(shí)現(xiàn)限流功能示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • golang讀取http的body時(shí)遇到的坑及解決

    golang讀取http的body時(shí)遇到的坑及解決

    這篇文章主要介紹了golang讀取http的body時(shí)遇到的坑及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • golang http 連接超時(shí)和傳輸超時(shí)的例子

    golang http 連接超時(shí)和傳輸超時(shí)的例子

    今天小編就為大家分享一篇golang http 連接超時(shí)和傳輸超時(shí)的例子,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-07-07
  • Go方法接收者值接收者與指針接收者詳解

    Go方法接收者值接收者與指針接收者詳解

    這篇文章主要為大家介紹了Go方法接收者值接收者與指針接收者詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • 詳解Go語(yǔ)言中用 os/exec 執(zhí)行命令的五種方法

    詳解Go語(yǔ)言中用 os/exec 執(zhí)行命令的五種方法

    這篇文章主要介紹了Go語(yǔ)言中用 os/exec 執(zhí)行命令的五種方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-11-11
  • Go語(yǔ)言學(xué)習(xí)之反射的用法詳解

    Go語(yǔ)言學(xué)習(xí)之反射的用法詳解

    反射指的是運(yùn)行時(shí)動(dòng)態(tài)的獲取變量的相關(guān)信息。本文將為大家詳細(xì)介紹Go語(yǔ)言中反射的用法,文中的示例代碼講解詳細(xì),感興趣的可以了解一下
    2022-04-04
  • Go通過(guò)goroutine實(shí)現(xiàn)多協(xié)程文件上傳的基本流程

    Go通過(guò)goroutine實(shí)現(xiàn)多協(xié)程文件上傳的基本流程

    多協(xié)程文件上傳是指利用多線(xiàn)程或多協(xié)程技術(shù),同時(shí)上傳一個(gè)或多個(gè)文件,以提高上傳效率和速度,本文給大家介紹了Go通過(guò)goroutine實(shí)現(xiàn)多協(xié)程文件上傳的基本流程,需要的朋友可以參考下
    2024-05-05
  • golang中如何使用kafka方法實(shí)例探究

    golang中如何使用kafka方法實(shí)例探究

    Kafka是一種備受歡迎的流處理平臺(tái),具備分布式、可擴(kuò)展、高性能和可靠的特點(diǎn),在處理Kafka數(shù)據(jù)時(shí),有多種最佳實(shí)踐可用來(lái)確保高效和可靠的處理,這篇文章將介紹golang中如何使用kafka方法
    2024-01-01
  • 以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

最新評(píng)論