<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          慎寫指針類型的全局變量

          共 5898字,需瀏覽 12分鐘

           ·

          2023-09-01 10:20

          簡(jiǎn)述:


          關(guān)于range二三事[1] 第二個(gè)case中,介紹了對(duì)于指針類型的 切片/map變量A 的循環(huán),要格外注意, 迭代出的value作用域是整個(gè)方法而非循環(huán)體內(nèi).

          改進(jìn)辦法:在循環(huán)體中引入中間變量,"暫存"下每次迭代的value的值


          但對(duì)于這個(gè)A,如果是全局變量,則又極有可能出現(xiàn)問(wèn)題:

                
                package main

          import (
           "fmt"
          )

          type UserInfo struct {
           Name string
           Age  int
          }

          var (
           defaultInfo    = UserInfo{Name: "fliter", Age: 26}
           defaultInfoSli = []*UserInfo{&defaultInfo}
          )

          func main() {
           
           for _, v := range defaultInfoSli {
            tmp := v

            //go func() {
            tmp.Age = 100
            //}()
           }
           //time.Sleep(1e9)
           fmt.Println(defaultInfoSli[0].Age)
          }

          defaultInfoSli迭代出的v為指針類型,tmp仍為指針類型,對(duì)其賦值,會(huì)改變?nèi)肿兞?strong style="color:rgb(22,94,202);">defaultInfoSli的值




          復(fù)現(xiàn):


          在具體業(yè)務(wù)場(chǎng)景中,服務(wù)啟動(dòng)時(shí)初始化(取數(shù)據(jù)庫(kù)或redis,或讀取配置文件,加載到內(nèi)存中)了一個(gè)全局變量.每個(gè)http請(qǐng)求過(guò)來(lái),golang都會(huì)有一個(gè)新的協(xié)程去處理相關(guān)邏輯. 對(duì)于某個(gè)具體方法內(nèi)的變量,對(duì)每次請(qǐng)求都是獨(dú)立和隔離(每次請(qǐng)求都相當(dāng)于一個(gè)個(gè)cellar,彼此之間不會(huì)有干涉和影響), 但對(duì)于永久存在內(nèi)存中的全局變量,如果有對(duì)其寫操作,每次請(qǐng)求都會(huì)影響該全局變量. 當(dāng)出現(xiàn)并發(fā)請(qǐng)求如用戶x和y同時(shí)請(qǐng)求接口, 兩次請(qǐng)求都會(huì)改寫全局變量, 這時(shí)就很可能出現(xiàn)返回的x和y的數(shù)據(jù)錯(cuò)亂


          Demo如下:

                
                package main

          import (
           "encoding/json"
           "fmt"
           "github.com/davecgh/go-spew/spew"
           "log"
           "net/http"
          )

          type BookInfo struct {
           Title string
           Rank  int
           Data  interface{}
          }

          var (
           defaultBook1 = BookInfo{Title: "水滸傳", Rank: 1}
           defaultBook2 = BookInfo{Title: "三國(guó)演義", Rank: 2}
           defaultBook3 = BookInfo{Title: "西游記", Rank: 3}
           defaultBook4 = BookInfo{Title: "紅樓夢(mèng)", Rank: 4}

           DefaultBookSli = []*BookInfo{&defaultBook1, &defaultBook2, &defaultBook3, &defaultBook4}
          )

          type CommonParams struct {
           ID   int64
           Name string
          }

          var (
           ModuleHandlers = map[int]func(params *CommonParams) *BookInfo{
            1: HandleTypeOne,
            2: HandleTypeTwo,
            3: HandleTypeThree,
            4: HandleTypeFour,
           }
          )

          func main() {

           fmt.Println(DefaultBookSli)

           http.HandleFunc("/index", deal) //設(shè)置訪問(wèn)的路由

           err := http.ListenAndServe(":80"nil//設(shè)置監(jiān)聽(tīng)的端口
           if err != nil {
            log.Fatal("ListenAndServe: ", err)
           }

          }

          func deal(w http.ResponseWriter, r *http.Request) {

           name := r.URL.Query().Get("name")

           //fmt.Println("name值為:", name)

           par := &CommonParams{
            ID:   0,
            Name: name,
           }

           // 獲取相關(guān)數(shù)據(jù)
           for _, v := range DefaultBookSli {

            module := v

            //fmt.Println("module is:", module)
            //fmt.Println("排序?yàn)?", module.Rank)

            m := ModuleHandlers[module.Rank](par "module.Rank")

            // 填充模塊數(shù)據(jù)
            if m.Data != nil {
             module.Data = m.Data
            }

            // (如果需要),重寫模塊標(biāo)題(在此不需要)
            //if m.Title != "" {
            // module.Title = m.Title
            //}
           }

           //time.Sleep(1e9) //此處等待并不是因?yàn)閰f(xié)程,而是方便測(cè)試,不加這個(gè)等待,執(zhí)行100次秒速就完成. 加這個(gè)等待是為了方便模擬"幾個(gè)用戶同時(shí)請(qǐng)求"
           //fmt.Println(DefaultBookSli[0].Rank)

           spew.Dump(DefaultBookSli)

           rsJson, _ := json.Marshal(DefaultBookSli)

           //fmt.Println(string(rsJson))    //這個(gè)寫入到w的是輸出到客戶端的
           fmt.Fprintf(w, string(rsJson)) //這個(gè)寫入到w的是輸出到客戶端的

          }

          func HandleTypeOne(p *CommonParams) *BookInfo {

           res := ""
           if p.Name == "施耐庵" {
            res = "我叫施耐庵,我是作者!"
           }
           return &BookInfo{Data: res}
          }

          func HandleTypeTwo(p *CommonParams) *BookInfo {

           res := ""
           if p.Name == "羅貫中" {
            res = "我叫羅貫中,我是作者!"
           }
           return &BookInfo{Data: res}
          }

          func HandleTypeThree(p *CommonParams) *BookInfo {

           res := ""
           if p.Name == "吳承恩" {

            res = "我叫吳承恩,我是作者!"
           }
           return &BookInfo{Data: res}
          }

          func HandleTypeFour(p *CommonParams) *BookInfo {

           res := ""
           if p.Name == "曹雪芹" {
            res = "我叫曹雪芹,我是作者!"
           }

           return &BookInfo{Data: res}
          }


          帶著參數(shù)x, 使用Postman進(jìn)行串行調(diào)用[2]100次,

          4dbba294c517da8d436bd693f2d0b54c.webp

          同時(shí)再訪問(wèn)這個(gè)接口,帶參數(shù)y,此時(shí)可以發(fā)現(xiàn),出現(xiàn)了數(shù)據(jù)錯(cuò)亂:

          f50d8679f368047d265242616f15f07a.webp


          修改方案:


          module := v這一步,實(shí)際上module依然是指針類型.

          可以module := *v,這樣module就不是指針類型,也就不會(huì)出現(xiàn)如上問(wèn)題.


          當(dāng)時(shí)問(wèn)題緊急,直接在里面新加了一個(gè)臨時(shí)變量,即:

                
                 // 獲取相關(guān)數(shù)據(jù)
           for _, v := range DefaultBookSli {

            module := v

            var temModule = &BookInfo{
             Title: module.Title,
             Rank:  module.Rank,
            }
            
            m := ModuleHandlers[temModule.Rank](par "temModule.Rank")

            // 填充模塊數(shù)據(jù)
            if m.Data != nil {
             module.Data = m.Data
            }
           }



          詳細(xì)過(guò)程參見(jiàn) 私有筆記 并發(fā)寫全局變量導(dǎo)致的數(shù)據(jù)錯(cuò)亂問(wèn)題[3],印象深刻的一次體驗(yàn)

          參考資料

          [1]

          關(guān)于range二三事: https://dashen.tech/2018/10/03/%E5%85%B3%E4%BA%8Erange%E4%BA%8C%E4%B8%89%E4%BA%8B/

          [2]

          Postman進(jìn)行串行調(diào)用: https://www.cnblogs.com/stm32stm32/p/10434399.html

          [3]

          并發(fā)寫全局變量導(dǎo)致的數(shù)據(jù)錯(cuò)亂問(wèn)題: https://note.youdao.com/web/#/file/WEB058d4e136c3ee281320806fd45e3b07a/note/WEB6c29aebf58ba0868bdeef60f7e6bac40/

          想要了解Go更多內(nèi)容,歡迎掃描下方??關(guān)注公眾號(hào), 回復(fù)關(guān)鍵詞 [實(shí)戰(zhàn)群]   ,就有機(jī)會(huì)進(jìn)群和我們進(jìn)行交流



          分享、在看與點(diǎn)贊Go  e99c582c070f7325241d4839bca7061d.webp
          瀏覽 37
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产精品美女久久久久久久久 | 无码在线中文字幕 | 日韩中出视频 | 亚洲天堂777 | www国产无码内射 |