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

Go defer與time.sleep的使用與區(qū)別

 更新時間:2024年04月19日 10:59:44   作者:寸 鐵  
本文主要介紹了Go defer與time.sleep的使用與區(qū)別,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

請大家看下面這段代碼,看運行結果會出現(xiàn)什么,為什么?

問題

demo

package main

import (
	"log"
	"time"
)

func main() {
	start := time.Now()

	defer func() {
		log.Printf("匿名函數(shù)時間差: %v", time.Since(start))
	}()

	defer log.Printf("時間差: %v", time.Since(start))
	time.Sleep(3 * time.Second)
	log.Printf("函數(shù)結束")
}

這里不少同學會認為:這題我會,先輸出函數(shù)結束,待整個函數(shù)結束后,根據(jù)defer后進先出的順序依次打印計算的時間差,由于這里睡眠了3秒,兩個時間差應該都是3秒左右。

所以答案為:

函數(shù)結束
時間差:3s
匿名函數(shù)時間差:3s

看到這里,我想說同學你的思路和想法是好的,一開始我也和你一樣,但是這里的答案是錯的,那為什么錯呢?總得有個原因吧!很明顯匿名函數(shù)的時間差符合邏輯是對的,那為什么時間差與預期不符合呢?下面我來進行分析。

運行結果

在這里插入圖片描述

分析

這里的關鍵點在于為什么時間差0,也就是說為什么時間差這個defer語句沒有被time.sleep 所影響?進一步分析,就是查看start賦值時機在哪?是在一開始調(diào)用defer就賦值,還是說在函數(shù)結束后給defer中的start賦值,從而造成結果的不同。
帶著這個問題,不妨來debug一下,看一下函數(shù)語句的執(zhí)行順序。

設置斷點

要想看一下start的賦值時機,設置斷點在出現(xiàn)start變量前面即可。

在這里插入圖片描述

設置好斷點后,開始進行debug

debug查看

step1

一開始,先初始化start的值

在這里插入圖片描述

step2

接著,來到第一個defer + 匿名函數(shù),看它有沒有進去里面的printf語句給start變量進行賦值,step over 直接跳過了匿名函數(shù)defer語句,也就是說明并沒有給匿名函數(shù)中的defer語句中的start賦值

在這里插入圖片描述

step3

之后,進入defer語句中,給time.Since中的start進行賦值。

這里,會發(fā)現(xiàn)問題的關鍵所在,對比defer + 匿名函數(shù) 一開始調(diào)用(非執(zhí)行)時,不會對里面的變量(參數(shù))start進行賦值。

然而普通的defer + printf則在一開始時就會對里面的變量start賦值,賦值后不會先time.since的結果計算出來,會在defer調(diào)用后,也就是光標移動到下一條語句時調(diào)用time.sleep計算時間差,其結果就是0s 左右,此時不會在調(diào)用時輸出,待整個函數(shù)執(zhí)行完畢退出后,依次按照defer 順序輸出。

在這里插入圖片描述

step4

進一步來到了time.sleep ,這也說明step3 確實給defer 語句賦值了,并沒有跳過,現(xiàn)在開始休眠3 s, 觀察3s后會光標會去到哪里?

猜想:3s 后會先輸出函數(shù)結束 ,之后函數(shù)退出,開始執(zhí)行defer 語句.
結合匿名函數(shù)的時間差為3s左右,又因為defer +匿名函數(shù)中的start 還未賦值,會回到開頭的defer + 匿名函數(shù)進行賦值。

在這里插入圖片描述

等待3s鐘

在這里插入圖片描述

step5

3s后,來到了輸出函數(shù)結束的語句。

在這里插入圖片描述

進一步函數(shù)退出,也就是整個函數(shù)執(zhí)行完畢!

在這里插入圖片描述

step6

又回到了剛才的defer + 匿名函數(shù)果然與step4的猜想一致!

在這里插入圖片描述

關鍵點來了!如下圖:這里會進入defer + 匿名函數(shù) , 并給里面的start變量賦值。
注意,這里傳參start 還是一開始的start! 只不過time.since(start) 的time 增加(休眠)了3s ,這也很好的解釋了為什么defer + 匿名函數(shù) 輸出的是3s 左右,而defer + printf 語句卻是輸出0s 左右。

在這里插入圖片描述

之后匿名函數(shù)結束

在這里插入圖片描述

退出當前的整個函數(shù)

在這里插入圖片描述

最后整個函數(shù)結束,輸出debug 的結果:這里加了一些debug 調(diào)試的時間,以運行結果為準,見下。

在這里插入圖片描述

運行結果如下:

在這里插入圖片描述

探討

debug 后,我們來探討一下為什么會出現(xiàn)這樣的情況?為什么結果會有所不同?里面的機制是什么?

defer + 輸出語句

傳值時機:一開始調(diào)用defer 時傳入

Go 語言中所有的函數(shù)調(diào)用都是傳值的

雖然 defer 是關鍵字,但是也繼承了這個特性。假設我們想要計算 main 函數(shù)運行的時間,可能會寫出以下的代碼:

package main

import (
    "fmt"
    "time"
)

func main() {
    start := time.Now()
    // 這里誤以為:startedAt是在time.Sleep之后才會將參數(shù)傳遞給defer所在語句的函數(shù)中
    defer fmt.Println(time.Since(start))

    time.Sleep(3 * time.Second)
}

關鍵點:調(diào)用defer關鍵字會立刻拷貝函數(shù)中引用的外部參數(shù)

所以 time.Since(start) 的結果不是在 main 函數(shù)退出之前計算的,而是在 defer 關鍵字調(diào)用時賦值計算的,最終導致上述代碼輸出 0s。

defer + 匿名函數(shù)

傳值時機:main函數(shù)結束后,執(zhí)行defer函數(shù) 時傳入,傳入函數(shù)指針

package main

import (
    "fmt"
    "time"
)

func main() {
    start := time.Now()
    // 使用匿名函數(shù),傳遞的是函數(shù)的指針
    defer func() {
        fmt.Println(time.Since(start))
    }()

    time.Sleep(3 * time.Second)
}

那為什么使用匿名函數(shù)就可以輸出3s 呢?關鍵點:defer 使用匿名函數(shù) , 傳遞的是函數(shù)的指針(函數(shù)是一種指針類型),結合剛才的斷點分析,不會在一開始調(diào)用defer + 匿名函數(shù)的時候就直接賦值,而是在后面main函數(shù)退出時,再執(zhí)行defer +匿名函數(shù) 時再傳入函數(shù)的指針,并給start變量賦值。

總結

總而言之:一開始調(diào)用defer+輸出語句會進行傳值,會在調(diào)用defer 的時候就直接傳遞值的拷貝(如剛才的defer+輸出語句),從而計算時間差。
調(diào)用defer + 匿名函數(shù) 傳遞的是指針類型(如函數(shù)、匿名函數(shù)), 會在main函數(shù)退出時,在執(zhí)行defer +匿名函數(shù) 時再傳入函數(shù)的指針,并給里面的變量賦值。

歸根結底,是傳入的類型不同,繼而導致傳入變量的時機不同,造成輸出的結果不同!

擴展

面試官:請你用defertime.sleep寫一個計算函數(shù)運行時間的程序
相信看到這里的小伙伴,已經(jīng)很清楚要寫什么代碼了!

package main

import (
    "fmt"
    "time"
)

func main() {
    start := time.Now()
    // 使用匿名函數(shù),傳遞的是函數(shù)的指針
    defer func() {
        fmt.Println(time.Since(start))
    }()

    time.Sleep(3 * time.Second)
}

到此這篇關于Go defer與time.sleep的使用與區(qū)別的文章就介紹到這了,更多相關Go defer與time.sleep內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家! 

相關文章

  • 深入了解Golang中reflect反射基本原理

    深入了解Golang中reflect反射基本原理

    反射是這樣一種機制,它是可以讓我們在程序運行時(runtime)訪問、檢測和修改對象本身狀態(tài)或行為的一種能力。本文主要帶大家來看看Golang中reflect反射基本原理,需要的可以參考一下
    2023-01-01
  • 詳解Golang?Map中的key為什么是無序的

    詳解Golang?Map中的key為什么是無序的

    本文主要介紹了Golang?Map中的key為什么是無序的,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • Golang中HTTP路由設計的使用與實現(xiàn)

    Golang中HTTP路由設計的使用與實現(xiàn)

    這篇文章主要介紹了Golang中HTTP路由設計的使用與實現(xiàn),為什么要設計路由規(guī)則,因為路由規(guī)則是HTTP的請求按照一定的規(guī)則 ,匹配查找到對應的控制器并傳遞執(zhí)行的邏輯,需要的朋友可以參考下
    2023-05-05
  • golang中的defer函數(shù)理解

    golang中的defer函數(shù)理解

    defer是Go語言中的延遲執(zhí)行語句,用來添加函數(shù)結束時執(zhí)行的代碼,常用于釋放某些已分配的資源、關閉數(shù)據(jù)庫連接、斷開socket連接、解鎖一個加鎖的資源,這篇文章主要介紹了golang中的defer函數(shù)理解,需要的朋友可以參考下
    2022-10-10
  • Golang如何快速刪除map所有元素

    Golang如何快速刪除map所有元素

    這篇文章主要介紹了Golang如何快速刪除map所有元素問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • 淺談Go語言中的接口類型

    淺談Go語言中的接口類型

    Go語言中接口是一種抽象的類型,本文主要介紹了淺談Go語言中的接口類型,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2024-08-08
  • gin項目部署到服務器并后臺啟動的步驟

    gin項目部署到服務器并后臺啟動的步驟

    本文主要介紹了gin項目部署到服務器并后臺啟動的步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-02-02
  • 淺析go語言如何實現(xiàn)協(xié)程的搶占式調(diào)度的

    淺析go語言如何實現(xiàn)協(xié)程的搶占式調(diào)度的

    go語言通過GMP模型實現(xiàn)協(xié)程并發(fā),為了避免單協(xié)程持續(xù)持有線程導致線程隊列中的其他協(xié)程饑餓問題,設計者提出了一個搶占式調(diào)度機制,本文會基于一個簡單的代碼示例對搶占式調(diào)度過程進行深入講解剖析
    2024-04-04
  • 深入理解Golang的單元測試和性能測試

    深入理解Golang的單元測試和性能測試

    Go語言提供了強大的測試工具,下面這篇文章主要給大家介紹了關于Golang單元測試和性能測試的相關資料,文中通過示例代碼給大家詳細介紹了單元測試和性能測試的相關內(nèi)容,需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-08-08
  • golang?pprof監(jiān)控memory?block?mutex統(tǒng)計原理分析

    golang?pprof監(jiān)控memory?block?mutex統(tǒng)計原理分析

    這篇文章主要為大家介紹了golang?pprof監(jiān)控memory?block?mutex統(tǒng)計原理分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-04-04

最新評論