阻塞佇列
阿新 • • 發佈:2020-08-22
閉包(英語:Closure),又稱詞法閉包(Lexical Closure)或函式閉包(function closures),是引用了自由變數的函式。這個被引用的自由變數將和這個函式一同存在,即使已經離開了創造它的環境也不例外。
-- wikipedia
閉包包含兩部分:
- 函式本身
- 函式所引用的環境
Go
裡閉包的函式必須是匿名函式。
/** 閉包:是引用了自由變數的函式。這個被引用的自由變數和這個函式一同存在,即使已經離開了建立的環境 */ func myFunc() func() int { foo := 0 //myFunc 裡面的匿名函式可以訪問並且更新 myFunc 裡面的變數,這個變數的生命週期因為匿名函式的存在而延長。 return func() int { foo++ return foo } } func main() { bar := myFunc() value_1 := bar() value_2 := bar() println(value_1) println(value_2) }
執行結果:
D:\GoWorkspace\src\polaris\main>go run closures.go
1
2
使用閉包實現斐波那契額
package main /** 使用閉包實現斐波那契額 */ func makeFibGen() func() int { f1 := 0 f2 := 1 return func() (fib int) { fib = f1 f2, f1 = (f1 + f2), f2 return fib } } func main() { gen := makeFibGen() for i := 0; i < 10; i++ { println(gen()) } }
Go 中匿名函式的實現
package main
/**
匿名函式的實現
*/
func myFunc1(message int) {
println(message)
}
func main() {
f := func(message int) {
println(message)
}
f(0x100)
myFunc1(0x100)
}
編譯:
D:\GoWorkspace\src\polaris\main>go build -gcflags "-N -l -m" -o test2 # _/D_/GoWorkspace/src/polaris/main .\test2.go:11:7: func literal does not escape
然後我們通過 go 提供的反彙編工具,反編譯我們剛剛生成的 test 檔案。
D:\GoWorkspace\src\polaris\main>go tool objdump -s "main\.main" ./test2
TEXT main.main(SB) D:/GoWorkspace/src/polaris/main/test2.go
test2.go:10 0x45ad00 65488b0c2528000000 MOVQ GS:0x28, CX
test2.go:10 0x45ad09 488b8900000000 MOVQ 0(CX), CX
test2.go:10 0x45ad10 483b6110 CMPQ 0x10(CX), SP
test2.go:10 0x45ad14 7642 JBE 0x45ad58
test2.go:10 0x45ad16 4883ec18 SUBQ $0x18, SP
test2.go:10 0x45ad1a 48896c2410 MOVQ BP, 0x10(SP)
test2.go:10 0x45ad1f 488d6c2410 LEAQ 0x10(SP), BP
test2.go:11 0x45ad24 488d159d000200 LEAQ go.func.*+77(SB), DX
test2.go:11 0x45ad2b 4889542408 MOVQ DX, 0x8(SP)
test2.go:14 0x45ad30 48c7042400010000 MOVQ $0x100, 0(SP)
test2.go:14 0x45ad38 488b0589000200 MOVQ go.func.*+77(SB), AX
test2.go:14 0x45ad3f ffd0 CALL AX
test2.go:15 0x45ad41 48c7042400010000 MOVQ $0x100, 0(SP)
test2.go:15 0x45ad49 e852ffffff CALL main.myFunc1(SB)
test2.go:16 0x45ad4e 488b6c2410 MOVQ 0x10(SP), BP
test2.go:16 0x45ad53 4883c418 ADDQ $0x18, SP
test2.go:16 0x45ad57 c3 RET
test2.go:10 0x45ad58 e81373ffff CALL runtime.morestack_noctxt(SB)
test2.go:10 0x45ad5d eba1 JMP main.main(SB)
:-1 0x45ad5f cc INT $0x3
TEXT main.main.func1(SB) D:/GoWorkspace/src/polaris/main/test2.go
test2.go:11 0x45ad60 65488b0c2528000000 MOVQ GS:0x28, CX
test2.go:11 0x45ad69 488b8900000000 MOVQ 0(CX), CX
test2.go:11 0x45ad70 483b6110 CMPQ 0x10(CX), SP
test2.go:11 0x45ad74 7635 JBE 0x45adab
test2.go:11 0x45ad76 4883ec10 SUBQ $0x10, SP
test2.go:11 0x45ad7a 48896c2408 MOVQ BP, 0x8(SP)
test2.go:11 0x45ad7f 488d6c2408 LEAQ 0x8(SP), BP
test2.go:12 0x45ad84 e84731fdff CALL runtime.printlock(SB)
test2.go:12 0x45ad89 488b442418 MOVQ 0x18(SP), AX
test2.go:12 0x45ad8e 48890424 MOVQ AX, 0(SP)
test2.go:12 0x45ad92 e80939fdff CALL runtime.printint(SB)
test2.go:12 0x45ad97 e8d433fdff CALL runtime.printnl(SB)
test2.go:12 0x45ad9c e8bf31fdff CALL runtime.printunlock(SB)
test2.go:13 0x45ada1 488b6c2408 MOVQ 0x8(SP), BP
test2.go:13 0x45ada6 4883c410 ADDQ $0x10, SP
test2.go:13 0x45adaa c3 RET
test2.go:11 0x45adab e8c072ffff CALL runtime.morestack_noctxt(SB)
test2.go:11 0x45adb0 ebae JMP main.main.func1(SB)
一共有三次 CALL
, 排除調最後那個 runtime
的 CALL
,剩下兩次分別對應了匿名函式呼叫以及正常的函式呼叫。而兩次的區別在於正常的函式是 CALL main.myFunc(SB)
, 匿名函式的呼叫是 CALL BX
。這兩種不同的呼叫方式意味著什麼?我們可以通過 gdb 來動態的跟蹤這段程式碼來具體分析一下。
注意事項
- 匿名函式作為返回物件效能上要比正常的函式效能要差。
- 閉包可能會導致變數逃逸到堆上來延長變數的生命週期,給 GC 帶來壓力。
參考:
http://sunisdown.me/closures-in-go.html
https://gobyexample.com/closures