<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      為什么使用ioutil.ReadAll 函數需要注意

      1. 引言

      當我們需要將數據一次性加載到內存中,ioutil.ReadAll 函數是一個方便的選擇,但是ioutil.ReadAll 的使用是需要注意的。

      在這篇文章中,我們將首先對ioutil.ReadAll函數進行基本介紹,之后會介紹其存在的問題,以及引起該問題的原因,最后給出了ioutil.ReadAll 函數的替代操作。通過這些內容,希望能幫助你更好地理解和使用ioutil.ReadAll 函數。

      2. 基本說明

      ioutil.ReadAll其實是標準庫的一個函數,其作用是從Reader 參數讀取所有的數據,直到遇到EOF為止,函數定義如下:

      func ReadAll(r io.Reader) ([]byte, error) 
      

      其中r 為待讀取數據的Reader,數據讀取結果將以字節切片的形式來返回,如果讀取過程中遇到了錯誤,也會返回對應的錯誤。

      下面通過一個簡單的示例,來簡單說明ioutil.ReadAll 函數的使用:

      package main
      
      import (
              "fmt"
              "io/ioutil"
              "os"
      )
      
      func main() {
              filePath := "example.txt"
      
              // 打開文件
              file, err := os.Open(filePath)
              if err != nil {
                    fmt.Println("無法打開文件:%s", err)
                    return
              }
              defer file.Close()
      
              // 讀取文件全部數據
              data, err := ioutil.ReadAll(file)
              if err != nil {
                      fmt.Println("無法讀取文件:%s", err)
                      return
              }
      
              // 將讀取到的數據轉換為字符串并輸出
              content := string(data)
              fmt.Println("文件內容:")
              fmt.Println(content)
      }
      

      在這個示例中,我們使用os.Open 函數打開指定路徑的文件,獲取到一個os.File 對象,接著,調用 ioutil.ReadAll 便能讀取到文件的全部數據。

      3. 為什么使用 ioutil.ReadAll 需要注意

      從上面的基本說明我們可以得知,ioutil.ReadAll 的作用是讀取指定數據源的全部數據,并將其以字節數組的形式來返回。比如,我們想要將整個文件的數據加載到內存中,此時就可以使用 ioutil.ReadAll 函數來實現。

      那這里就有一個問題, 加載一份數據到內存中,會耗費多少內存資源呢? 按照我們的理解,正常是數據源數據有多大,就大概消耗多大的內存資源。

      然而,如果使用 ioutil.ReadAll 函數加載數據時消耗的內存資源,可能與我們的想法存在一些差距。通常使用 ioutil.ReadAll 函數加載全部數據有可能會消耗更多的內存。

      下面我們創建一個10M的文件,然后寫一個基準測試函數,來展示使用 ioutil.ReadAll 加載整個文件的數據,需要分配多少內存,函數如下:

      func BenchmarkReadAllMemoryUsage(b *testing.B) {
         filePath := "largefile.txt"
      
         for n := 0; n < b.N; n++ {
            // 打開文件
            file, err := os.Open(filePath)
            if err != nil {
               fmt.Println("無法打開文件:%r", err)
               return
            }
            defer file.Close()
            _, err = ioutil.ReadAll(file)
            if err != nil {
               b.Fatal(err)
            }
         }
      }
      

      基準測試的運行結果如下:

      BenchmarkReadAllMemoryUsage-4                106          14385391 ns/op        52263424 B/op         42 allocs/op
      

      其中106,表示基準測試的迭代次數,14385391 ns/op, 表示每次迭代的平均執行時間,52263424 B/op表示每次迭代的平均內存分配量,42 allocs/op 表示每次迭代的平均分配次數,

      上面基準測試的結果,我們主要關注每次迭代需要消耗的內存量,也就是 52263424 B/op 這個數據,這個大概相當于50M左右。在這個示例中,我們使用 ioutil.ReadAll 加載一個10M大小的文件,此時需要分配50M的內存,是文件大小的5倍。

      從這里我們可以看出,使用ioutil.ReadAll 加載數據時,存在的一個注意點,便是其分配的內存遠遠大于待加載數據的大小。

      那我們就有疑問了,為什么 ioutil.ReadAll 加載數據時,會消耗這么多內存呢? 下面我們通過說明ioutil.ReadAll 函數的實現,來解釋其中的原因。

      4. 為什么這么消耗內存

      ioutil.ReadAll 函數的實現其實比較簡單,ReadAll 函數會初始化一個字節切片緩沖區,然后調用源ReaderRead 方法不斷讀取數據,直接讀取到EOF 為止。

      不過需要注意的是,ReadAll 函數初始化的緩沖區,其初始化大小只有512個字節,在讀取過程中,如果緩沖區長度不夠,將會不斷擴容該緩沖區,直到緩沖區能夠容納所有待讀取數據為止。所以調用ioutil.ReadAll 可能會存在多次內存分配的現象。下面我們來看其代碼實現:

      func ReadAll(r Reader) ([]byte, error) {
         // 初始化一個 512 個字節長度的 字節切片
         b := make([]byte, 0, 512)
         for {
            // len(b) == cap(b),此時緩沖區已滿,需要擴容
            if len(b) == cap(b) {
               // 首先append(b,0), 觸發切片的擴容機制
               // 然后再去掉前面 append 的 '0' 字符
               b = append(b, 0)[:len(b)]
            }
            // 調用Read 方法讀取數據
            n, err := r.Read(b[len(b):cap(b)])
            // 更新切片 len 字段的值
            b = b[:len(b)+n]
            if err != nil {
               // 讀取到 EOF, 此時直接返回
               if err == EOF {
                  err = nil
               }
               return b, err
            }
         }
      }
      

      從上面代碼實現來看,使用 ioutil.ReadAll 加載數據需要分配大量內存的原因是因為切片的不斷擴容導致的。

      ioutil.ReadAll 加載數據時,一開始只初始化了一個512字節大小的切片,如果待加載的數據超過512字節的話,切片會觸發擴容操作。同時其也不是一次性擴容到能夠容納所有數據的長度,而是基于切片的擴容機制來決定的。接下來可能會擴容到1024個字節,會重新申請一塊內存空間,然后將原切片數據拷貝過去。

      之后如果數據超過1024個字節,切片會繼續擴容的操作,如此反復,直到切片能夠容納所有的數據為止,這個過程中會存在多次的內存分配的操作,導致大量內存的消耗。

      因此,當使用 ioutil.ReadAll加載數據時,內存消耗會隨著數據的大小而增加。特別是在處理大文件或大數據集時,可能需要分配大量的內存空間。這就解釋了為什么僅加載一個10M大小的文件,就需要分配50M內存的現象。

      5. 替換操作

      既然 ioutil.ReadAll 這么消耗內存,那么我們應該盡量避免對其進行使用。但是有時候,我們又需要讀取全部數據到內存中,這個時候其實可以使用其他函數來替代ioutil.ReadAll。下面從文件讀取和網絡IO讀取這兩個方面來進行介紹。

      5.1 文件讀取

      ioutil 工具包中,還存在一個ReadFile的工具函數,能夠加載文件的全部數據到內存中,函數定義如下:

      func ReadFile(filename string) ([]byte, error) {}
      

      ReadFile函數的使用非常簡單,只需要傳入一個待加載文件的路徑,返回的數據為文件的內容。下面通過一個基準函數,展示其加載文件時需要的分配內存數等的數據,來和ioutil.ReadAll做一個比較:

      func BenchmarkReadFileMemoryUsage(b *testing.B) {
         filePath := "largefile.txt"
         for n := 0; n < b.N; n++ {
            _, err := ioutil.ReadFile(filePath)
            if err != nil {
               b.Fatal(err)
            }
         }
      }
      

      上面基準測試運行結果如下:

      // ReadFile 函數基準測試結果
      BenchmarkReadFileMemoryUsage-4                592           1942212 ns/op        10494290 B/op          5 allocs/op
      // ReadAll 函數基準測試結果
      BenchmarkReadAllMemoryUsage-4                106          14385391 ns/op        52263424 B/op         42 allocs/op
      

      使用ReadFile加載整個文件的數據,分配的內存數大概也為10M左右,同時執行時間和內存分配次數,也相對于ReadAll 函數來看,也相對更小。

      因此,如果我們確實需要加載文件的全部數據,此時使用ReadFile相對于ReadAll 肯定是更為合適的。

      5.2 網絡IO讀取

      如果是網絡IO操作,此時我們需要假定一個前提,是所有的響應數據,應該都是有響應頭的,能夠通過響應頭,獲取到響應體的長度,然后再基于此讀取全部響應體的數據。

      這里可以使用io.Copy函數來將數據拷貝,從而來替代ioutil.ReadAll,下面是一個大概代碼結構:

      package main
      
      import (
              "bytes"
              "fmt"
              "io"
              "os"
      )
      
      func main() {
              // 1. 建立一個網絡連接
              src := xxx
              defer src.Close()
              // 2. 讀取報文頭,獲取請求包的長度
              size := xxx
              // 3. 基于該 size 創建一個 字節切片
              buf := make([]byte, size)
              buffer := bytes.NewBuffer(buf)
              // 4. 使用buffer來讀取數據
              _, err = io.Copy(&buffer, srcFile)
              if err != nil {
                      fmt.Println("Failed to copy data:", err)
                      return
              }
              // 現在數據已加載到內存中的緩沖區(buffer)中
              fmt.Println("Data loaded into buffer successfully.")
      }
      

      通過這種方式,能夠使用io.Copy 函數替換ioutil.ReadAll ,讀取到所有的數據,而io.Copy 函數不會存在 ioutil.ReadAll 函數存在的問題。

      6. 總結

      本文首先對 ioutil.ReadAll 進行了基本的說明,同時給了一個簡單的使用示例。

      隨后,通過基準測試展示了使用 ioutil.ReadAll 加載數據,消耗的內存可能遠遠大于待加載的數據。之后,通過對源碼講解,說明了導致這個現象導致的原因。

      最后,給出了一些替代方案,如使用 ioutil.ReadFile 函數和使用 io.Copy 函數等,以減少內存占用。基于以上內容,便完成了對ioutil.ReadAll 函數的介紹,希望對你有所幫助。

      posted @ 2023-07-11 07:39  菜鳥額  閱讀(493)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 亚洲国产另类久久久精品| 久久天堂综合亚洲伊人HD妓女| 爱性久久久久久久久| 国产精品欧美一区二区三区不卡 | 麻豆国产va免费精品高清在线| 国产美女69视频免费观看| 欧美国产日产一区二区| 综合久久国产九一剧情麻豆| 老色99久久九九爱精品| 精品国产中文字幕懂色| 欧美视频二区欧美影视| 国产精品呻吟一区二区三区| 国产对白老熟女正在播放| 亚洲日韩乱码中文无码蜜桃臀| 亚洲男人天堂东京热加勒比 | 精品午夜福利在线视在亚洲| 老熟妇乱子交视频一区| 国产熟女激情一区二区三区| 国产成人精品亚洲午夜| 啪啪av一区二区三区| 亚洲老熟女一区二区三区| 亚洲码国产精品高潮在线| 亚洲国产欧美日韩另类| 国产在线观看播放av| 亚洲中文字幕无码永久在线 | 国产亚洲色视频在线| 亚洲欧美日韩人成在线播放| 亚洲欧美在线综合一区二区三区| 少妇粗大进出白浆嘿嘿视频 | 日本一区二区三区视频版| 亚洲高清国产拍精品5G| jizzjizz日本高潮喷水| 国产精品久久久久无码网站| 国产精品无码无需播放器| 少妇人妻偷人免费观看| 成人精品一区日本无码网| 五月丁香综合缴情六月小说| 91福利视频一区二区| 成人国产av精品免费网| 亚洲中文字幕无码一区日日添| 国产成人高清亚洲综合|