Go語言defer分析

来源:https://www.cnblogs.com/enochzzg/archive/2020/03/24/12562634.html
-Advertisement-
Play Games

什麼是defer? defer語句是專門在函數結束以後做一些清理工作的。我們先舉一個例子來更好的理解,現在有一個函數,它的作用是把一個文件內容拷貝到另一個文件。 以上代碼是可以正常執行的,但是存在一個問題,如果os.Create執行失敗,那麼就無法執行到文件資源的Close函數。進程每打開一個文件就 ...


什麼是defer?

defer語句是專門在函數結束以後做一些清理工作的。我們先舉一個例子來更好的理解,現在有一個函數,它的作用是把一個文件內容拷貝到另一個文件。

func CopyFile(dstName string, srcName string) (written int64, err error) {
	src, err := os.Open(srcName)
	if err != nil {
		return
	}

	dst, err := os.Create(dstName)
	if err != nil {
		return
	}

	written, err = io.Copy(dst, src)
	src.Close()
	dst.Close()
	return
}

以上代碼是可以正常執行的,但是存在一個問題,如果os.Create執行失敗,那麼就無法執行到文件資源的Close函數。進程每打開一個文件就會占用一個文件描述符,而在系統當中,文件描述符是有上限的,可以通過ulimit -n查看,如果資源沒有被及時釋放,會出現資源浪費的情況。如果打開文件過多,也會出現Too many open files的提示。這個時候就需要通過defer來解決問題了,代碼如下。

func CopyFile(dstName string, srcName string) (written int64, err error) {
	src, err := os.Open(srcName)
	if err != nil {
		return
	}
	defer src.Close()

	dst, err := os.Create(dstName)
	if err != nil {
		return
	}
	defer dst.Close()

	written, err = io.Copy(dst, src)
	return
}

defer語句會在return參數設置之後、函數返回給調用者之前執行,這樣就不再擔心文件資源無法被Close了。

defer的三個規則

規則一:被deferred的函數參數在defer時確定

func a() {
	i := 0
	defer fmt.Println(i)
	i++
	return
}

我們通過以上代碼來理解這個拗口的規則,如果根據官方的定義來理解這段代碼,變數i的值鐵定為1,但實際執行的結果不是1,卻是0。

Each time a "defer" statement executes, the function value and parameters to the call are evaluated as usual and saved anew but the actual function is not invoked. Instead, deferred functions are invoked immediately before the surrounding function returns, in the reverse order they were deferred.

那我們再重新來理解這個規則,變數i是在逐行執行到defer語句的時候就已經確定了值,這個時候變數i還沒有進行自增,所以輸出的結果應該是0而不是1。

規則二:被deferred函數執行順序遵循LIFO原則

func b() {
    for i := 0; i < 4; i++ {
        defer fmt.Print(i)
    }
}

LIFO全稱為Last In First Out,意為後進先出,棧是一種典型的LIFO數據結構。defer也是如此,拿以上代碼為例,先後遍歷了四次,也就是做了四次壓棧操作。同理,在函數return之前,就會逐一齣棧,倒序執行defer語句,所以上述代碼輸出內容為3210

規則三:deferred函數可以讀取和修改函數的返回值

func c() (i int) {
	defer func() { i++ }()
	return 1
}

我們定義一個defer函數,將變數i自增,那麼最終變數i的值為2。我們從官方文檔中可以看出,defer語句是在函數設置返回值後,且在返回給主調函數前執行的,根據這個思路,c函數已經return了1,這個時候執行了defer函數,將返回的結果1進行了自增,然後返回給主調函數,這個時候主調函數拿到的值就是2了。

deferred functions are executed after any result parameters are set by that return statement but before the function returns to its caller


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、委派模式 委派模式(Delegate Pattern):指負責任務的調度和分配任務,跟代理模式很像,可以看做是一種特殊情況下的靜態代理的全權代理,但是代理模式註重過程,而委派模式註重結果。(屬於行為型模式,但它不屬於GOF的23種設計模式之一。類名以Delegate和Dispatcher結尾的一 ...
  • 圖解Java設計模式之組合模式 看一個學校院系展示需求 傳統方案解決學院院系展示(類圖) 傳統方案解決學校院系展示存在的問題分析 組合模式基本介紹 組合模式的原理類圖 組合模式解決學校院系展示的應用實例 組合模式在JDK集合的源碼分析 組合模式的註意事項和細節 看一個學校院系展示需求 編寫程式展示一 ...
  • 我相信大家都用過線程池,但是線程池數量設置為多少比較合理呢? 線程數的設置的最主要的目的是為了充分併合理地使用 CPU 和記憶體等資源,從而最大限度地提高程式的性能,因此讓我們一起去探索吧! 首先要考慮到 CPU 核心數,那麼在 Java 中如何獲取核心線程數? 可以使用 方法來獲取(可能不准確,作為 ...
  • E. Efficient Exchange 題意:這一題的題意簡單;簡單來說就是A、B有1元10元、100元、1000元。。。。等等10的整次冪的票額的紙幣,現在B付錢給A,問當中涉及錢的張數最少是多少可以把錢付清。 題解:這一題官方題解給的是DP+遞歸,自己看了半天,代碼中的註釋給出了自己的理解, ...
  • 1 簡介 項目越做越發覺得,任何一個系統上線,運維監控都太重要了。關於Springboot微服務的監控,之前寫過 "【Springboot】用Springboot Admin監控你的微服務應用" ,這個方案可以實時監控並提供告警提醒功能,但不能記錄歷史數據,無法查看過去1小時或過去1天等運維情況。本 ...
  • 一、對象類文件的序列換與反序列化 1.java.io.ObjectOutputStream;序列化JAVA對象到硬碟 2.java.io.ObjectInputStream;將硬碟中的數據“反序列化”到JVM記憶體中 Compile編譯(java->class) DeCompile反編譯(class- ...
  • 什麼場景該使用通用計價 如果商品的費用屬性一直在變化,比如隔三岔五的新增某種費用(按新規則計算的新費用),作為開發人員的你每次需要膽戰心驚的維護現有的計價介面,測試也需要花費大量時間驗證對其他費用的影響。基於這一點,我在想如果初期把計價做成一個通用的計價介面,每次加費用我只需要關註新費用的計算規則, ...
  • 繼承的優點:減少代碼的冗餘 提高代碼的重用性 派生類定義格式: Class 派生類名 : 繼承方式 基類名{ //派生類新增的數據成員和成員函數 }; class 子類: 繼承方式 父類名{ //子類新增的數據成員和成員函數 }; 繼承方式分類: public : 公有繼承 (重要) private ...
一周排行
    -Advertisement-
    Play Games
  • 前言 在我們開發過程中基本上不可或缺的用到一些敏感機密數據,比如SQL伺服器的連接串或者是OAuth2的Secret等,這些敏感數據在代碼中是不太安全的,我們不應該在源代碼中存儲密碼和其他的敏感數據,一種推薦的方式是通過Asp.Net Core的機密管理器。 機密管理器 在 ASP.NET Core ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 順序棧的介面程式 目錄順序棧的介面程式頭文件創建順序棧入棧出棧利用棧將10進位轉16進位數驗證 頭文件 #include <stdio.h> #include <stdbool.h> #include <stdlib.h> 創建順序棧 // 指的是順序棧中的元素的數據類型,用戶可以根據需要進行修改 ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • C總結與剖析:關鍵字篇 -- <<C語言深度解剖>> 目錄C總結與剖析:關鍵字篇 -- <<C語言深度解剖>>程式的本質:二進位文件變數1.變數:記憶體上的某個位置開闢的空間2.變數的初始化3.為什麼要有變數4.局部變數與全局變數5.變數的大小由類型決定6.任何一個變數,記憶體賦值都是從低地址開始往高地 ...
  • 如果讓你來做一個有狀態流式應用的故障恢復,你會如何來做呢? 單機和多機會遇到什麼不同的問題? Flink Checkpoint 是做什麼用的?原理是什麼? ...
  • C++ 多級繼承 多級繼承是一種面向對象編程(OOP)特性,允許一個類從多個基類繼承屬性和方法。它使代碼更易於組織和維護,並促進代碼重用。 多級繼承的語法 在 C++ 中,使用 : 符號來指定繼承關係。多級繼承的語法如下: class DerivedClass : public BaseClass1 ...
  • 前言 什麼是SpringCloud? Spring Cloud 是一系列框架的有序集合,它利用 Spring Boot 的開發便利性簡化了分散式系統的開發,比如服務註冊、服務發現、網關、路由、鏈路追蹤等。Spring Cloud 並不是重覆造輪子,而是將市面上開發得比較好的模塊集成進去,進行封裝,從 ...
  • class_template 類模板和函數模板的定義和使用類似,我們已經進行了介紹。有時,有兩個或多個類,其功能是相同的,僅僅是數據類型不同。類模板用於實現類所需數據的類型參數化 template<class NameType, class AgeType> class Person { publi ...
  • 目錄system v IPC簡介共用記憶體需要用到的函數介面shmget函數--獲取對象IDshmat函數--獲得映射空間shmctl函數--釋放資源共用記憶體實現思路註意 system v IPC簡介 消息隊列、共用記憶體和信號量統稱為system v IPC(進程間通信機制),V是羅馬數字5,是UNI ...