av激情亚洲男人的天堂国语,日韩欧美精品一中文字幕,无码av一区二区三区无码,国产又色又爽又刺激的a片,国产又色又爽又刺激的a片

你有考慮過(guò)DeferClose()的風(fēng)險(xiǎn)嗎

本文轉(zhuǎn)載自微信公眾號(hào)「Golang技術(shù)分享」,作者機(jī)器鈴砍菜刀 。轉(zhuǎn)載本文請(qǐng)聯(lián)系Golang技術(shù)分享公眾號(hào)。

10年積累的網(wǎng)站設(shè)計(jì)、網(wǎng)站制作經(jīng)驗(yàn),可以快速應(yīng)對(duì)客戶對(duì)網(wǎng)站的新想法和需求。提供各種問(wèn)題對(duì)應(yīng)的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認(rèn)識(shí)你,你也不認(rèn)識(shí)我。但先做網(wǎng)站后付款的網(wǎng)站建設(shè)流程,更有南潯免費(fèi)網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。

作為一名 Gopher,我們很容易形成一個(gè)編程慣例:每當(dāng)有一個(gè)實(shí)現(xiàn)了 io.Closer 接口的對(duì)象 x 時(shí),在得到對(duì)象并檢查錯(cuò)誤之后,會(huì)立即使用 defer x.Close() 以保證函數(shù)返回時(shí) x 對(duì)象的關(guān)閉 。以下給出兩個(gè)慣用寫(xiě)法例子。

HTTP 請(qǐng)求

 
 
 
 
  1. 1resp, err := http.Get("https://golang.google.cn/") 
  2. 2if err != nil { 
  3. 3    return err 
  4. 4} 
  5. 5defer resp.Body.Close() 
  6. 6// The following code: handle resp 

訪問(wèn)文件

 
 
 
 
  1. 1f, err := os.Open("/home/golangshare/gopher.txt") 
  2. 2if err != nil { 
  3. 3    return err 
  4. 4} 
  5. 5defer f.Close() 
  6. 6// The following code: handle f 

存在問(wèn)題

實(shí)際上,這種寫(xiě)法是存在潛在問(wèn)題的。defer x.Close() 會(huì)忽略它的返回值,但在執(zhí)行 x.Close() 時(shí),我們并不能保證 x 一定能正常關(guān)閉,萬(wàn)一它返回錯(cuò)誤應(yīng)該怎么辦?這種寫(xiě)法,會(huì)讓程序有可能出現(xiàn)非常難以排查的錯(cuò)誤。

那么,Close() 方法會(huì)返回什么錯(cuò)誤呢?在 POSIX 操作系統(tǒng)中,例如 Linux 或者 maxOS,關(guān)閉文件的 Close() 函數(shù)最終是調(diào)用了系統(tǒng)方法 close(),我們可以通過(guò) man close 手冊(cè),查看 close() 可能會(huì)返回什么錯(cuò)誤

 
 
 
 
  1. 1ERRORS 
  2. 2     The close() system call will fail if: 
  3. 4     [EBADF]            fildes is not a valid, active file descriptor. 
  4. 6     [EINTR]            Its execution was interrupted by a signal. 
  5. 8     [EIO]              A previously-uncommitted write(2) encountered an 
  6. 9                        input/output error. 

錯(cuò)誤 EBADF 表示無(wú)效文件描述符 fd,與本文中的情況無(wú)關(guān);EINTR 是指的 Unix 信號(hào)打斷;那么本文中可能存在的錯(cuò)誤是 EIO。

EIO 的錯(cuò)誤是指未提交讀,這是什么錯(cuò)誤呢?

計(jì)算機(jī)存儲(chǔ)層次結(jié)構(gòu)

EIO 錯(cuò)誤是指文件的 write() 的讀還未提交時(shí)就調(diào)用了 close() 方法。

上圖是一個(gè)經(jīng)典的計(jì)算機(jī)存儲(chǔ)器層級(jí)結(jié)構(gòu),在這個(gè)層次結(jié)構(gòu)中,從上至下,設(shè)備的訪問(wèn)速度越來(lái)越慢,容量越來(lái)越大。存儲(chǔ)器層級(jí)結(jié)構(gòu)的主要思想是上一層的存儲(chǔ)器作為低一層存儲(chǔ)器的高速緩存。

CPU 訪問(wèn)寄存器會(huì)非常之快,相比之下,訪問(wèn) RAM 就會(huì)很慢,而訪問(wèn)磁盤或者網(wǎng)絡(luò),那意味著就是蹉跎光陰。如果每個(gè) write() 調(diào)用都將數(shù)據(jù)同步地提交到磁盤,那么系統(tǒng)的整體性能將會(huì)極度降低,而我們的計(jì)算機(jī)是不會(huì)這樣工作的。當(dāng)我們調(diào)用 write() 時(shí),數(shù)據(jù)并沒(méi)有立即被寫(xiě)到目標(biāo)載體上,計(jì)算機(jī)存儲(chǔ)器每層載體都在緩存數(shù)據(jù),在合適的時(shí)機(jī)下,將數(shù)據(jù)刷到下一層載體,這將寫(xiě)入調(diào)用的同步、緩慢、阻塞的同步轉(zhuǎn)為了快速、異步的過(guò)程。

這樣看來(lái),EIO 錯(cuò)誤的確是我們需要提防的錯(cuò)誤。這意味著如果我們嘗試將數(shù)據(jù)保存到磁盤,在 defer x.Close() 執(zhí)行時(shí),操作系統(tǒng)還并未將數(shù)據(jù)刷到磁盤,這時(shí)我們應(yīng)該獲取到該錯(cuò)誤提示(只要數(shù)據(jù)還未落盤,那數(shù)據(jù)就沒(méi)有持久化成功,它就是有可能丟失的,例如出現(xiàn)停電事故,這部分?jǐn)?shù)據(jù)就永久消失了,且我們會(huì)毫不知情)。但是按照上文的慣例寫(xiě)法,我們程序得到的是 nil 錯(cuò)誤。

解決方案

我們針對(duì)關(guān)閉文件的情況,來(lái)探討幾種可行性改造方案

  • 第一種方案,那就是不使用 defer
 
 
 
 
  1.  1func solution01() error { 
  2.  2    f, err := os.Create("/home/golangshare/gopher.txt") 
  3.  3    if err != nil { 
  4.  4        return err 
  5.  5    } 
  6.  6 
  7.  7    if _, err = io.WriteString(f, "hello gopher"); err != nil { 
  8.  8        f.Close() 
  9.  9        return err 
  10. 10    } 
  11. 11 
  12. 12    return f.Close() 
  13. 13} 

這種寫(xiě)法就需要我們?cè)?io.WriteString 執(zhí)行失敗時(shí),明確調(diào)用 f.Close() 進(jìn)行關(guān)閉。但是這種方案,需要在每個(gè)發(fā)生錯(cuò)誤的地方都要加上關(guān)閉語(yǔ)句 f.Close(),如果對(duì) f 的寫(xiě)操作 case 較多,容易存在遺漏關(guān)閉文件的風(fēng)險(xiǎn)。

  • 第二種方案是,通過(guò)命名返回值 err 和閉包來(lái)處理
 
 
 
 
  1.  1func solution02() (err error) { 
  2.  2    f, err := os.Create("/home/golangshare/gopher.txt") 
  3.  3    if err != nil { 
  4.  4        return 
  5.  5    } 
  6.  6 
  7.  7    defer func() { 
  8.  8        closeErr := f.Close() 
  9.  9        if err == nil { 
  10. 10            err = closeErr 
  11. 11        } 
  12. 12    }() 
  13. 13 
  14. 14    _, err = io.WriteString(f, "hello gopher") 
  15. 15    return 
  16. 16} 

這種方案解決了方案一中忘記關(guān)閉文件的風(fēng)險(xiǎn),如果有更多 if err !=nil 的條件分支,這種模式可以有效降低代碼行數(shù)。

  • 第三種方案是,在函數(shù)最后 return 語(yǔ)句之前,顯示調(diào)用一次 f.Close()
 
 
 
 
  1.  1func solution03() error { 
  2.  2    f, err := os.Create("/home/golangshare/gopher.txt") 
  3.  3    if err != nil { 
  4.  4        return err 
  5.  5    } 
  6.  6    defer f.Close() 
  7.  7 
  8.  8    if _, err := io.WriteString(f, "hello gopher"); err != nil { 
  9.  9        return err 
  10. 10    } 
  11. 11 
  12. 12    if err := f.Close(); err != nil { 
  13. 13        return err 
  14. 14    } 
  15. 15    return nil 
  16. 16} 

這種解決方案能在 io.WriteString 發(fā)生錯(cuò)誤時(shí),由于 defer f.Close() 的存在能得到 close 調(diào)用。也能在 io.WriteString 未發(fā)生錯(cuò)誤,但緩存未刷新到磁盤時(shí),得到 err := f.Close() 的錯(cuò)誤,而且由于 defer f.Close() 并不會(huì)返回錯(cuò)誤,所以并不擔(dān)心兩次 Close() 調(diào)用會(huì)將錯(cuò)誤覆蓋。

  • 最后一種方案是,函數(shù) return 時(shí)執(zhí)行 f.Sync()
 
 
 
 
  1.  1func solution04() error { 
  2.  2    f, err := os.Create("/home/golangshare/gopher.txt") 
  3.  3    if err != nil { 
  4.  4        return err 
  5.  5    } 
  6.  6    defer f.Close() 
  7.  7 
  8.  8    if _, err = io.WriteString(f, "hello world"); err != nil { 
  9.  9        return err 
  10. 10    } 
  11. 11 
  12. 12    return f.Sync() 
  13. 13} 

由于調(diào)用 close() 是最后一次獲取操作系統(tǒng)返回錯(cuò)誤的機(jī)會(huì),但是在我們關(guān)閉文件時(shí),緩存不一定被會(huì)刷到磁盤上。那么,我們可以調(diào)用 f.Sync() (其內(nèi)部調(diào)用系統(tǒng)函數(shù) fsync )強(qiáng)制性讓內(nèi)核將緩存持久到磁盤上去。

 
 
 
 
  1.  1// Sync commits the current contents of the file to stable storage. 
  2.  2// Typically, this means flushing the file system's in-memory copy 
  3.  3// of recently written data to disk. 
  4.  4func (f *File) Sync() error { 
  5.  5    if err := f.checkValid("sync"); err != nil { 
  6.  6        return err 
  7.  7    } 
  8.  8    if e := f.pfd.Fsync(); e != nil { 
  9.  9        return f.wrapErr("sync", e) 
  10. 10    } 
  11. 11    return nil 
  12. 12} 

由于 fsync 的調(diào)用,這種模式能很好地避免 close 出現(xiàn)的 EIO??梢灶A(yù)見(jiàn)的是,由于強(qiáng)制性刷盤,這種方案雖然能很好地保證數(shù)據(jù)安全性,但是在執(zhí)行效率上卻會(huì)大打折扣。


本文名稱:你有考慮過(guò)DeferClose()的風(fēng)險(xiǎn)嗎
轉(zhuǎn)載注明:http://uogjgqi.cn/article/ccdssjs.html
掃二維碼與項(xiàng)目經(jīng)理溝通

我們?cè)谖⑿派?4小時(shí)期待你的聲音

解答本文疑問(wèn)/技術(shù)咨詢/運(yùn)營(yíng)咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流