1 回答

TA貢獻1860條經驗 獲得超9個贊
go
關鍵字在 Go 中的工作?原理,請參閱Go_statements:
函數值和參數在調用 goroutine 中照常計算,但與常規調用不同,程序執行不會等待調用的函數完成。相反,該函數開始在新的 goroutine 中獨立執行。當函數終止時,它的 goroutine 也會終止。如果函數有任何返回值,它們將在函數完成時被丟棄。
函數值和參數使用關鍵字進行評估go
(關鍵字相同,defer
請參閱關鍵字示例defer
)。
為了理解評估順序,讓我們嘗試一下:
go?have()(fun("with?Go."))
讓我們運行它并閱讀評估順序的代碼注釋:
package main
import (
? ? "fmt"
? ? "sync"
)
func main() {
? ? go have()(fun("with Go."))
? ? fmt.Print("some ") // evaluation order: ~ 3
? ? wg.Wait()
}
func have() func(string) {
? ? fmt.Print("Go ") // evaluation order: 1
? ? return funWithGo
}
func fun(msg string) string {
? ? fmt.Print("have ") // evaluation order: 2
? ? return msg
}
func funWithGo(msg string) {
? ? fmt.Println("fun", msg) // evaluation order: 4
? ? wg.Done()
}
func init() {
? ? wg.Add(1)
}
var wg sync.WaitGroup
輸出:
Go have some fun with Go.
解釋 go have()(fun("with Go.")):
首先進行就地評估:
go have()(...)第一have()部分運行,結果是fmt.Print("Go ")和return funWithGo,然后fun("with Go.")運行,結果是fmt.Print("have ")和return "with Go.";現在我們有了go funWithGo("with Go.")。
所以最后的goroutine調用是go funWithGo("with Go.")
這是一個啟動新 goroutine 的調用,所以我們真的不知道它什么時候會運行。那么就有機會運行下一行:fmt.Print("some "),那么我們在這里等待wg.Wait()?,F在 goroutine 運行這個funWithGo("with Go."),結果是fmt.Println("fun", "with Go.")then wg.Done();就這些。
讓我們重寫上面的代碼,只需將命名函數替換為匿名函數,因此這段代碼與上面相同:
例如參見:
func have() func(string) {
? ? fmt.Print("Go ") // evaluation order: 1
? ? return funWithGo
}
然后剪切這段代碼,選擇have其中的部分go have()并粘貼,然后選擇have其中的部分func have()并按下Delete鍵盤,然后你會得到這樣的:
這更漂亮,結果相同,只需將所有函數替換為匿名函數即可:
package main
import (
? ? "fmt"
? ? "sync"
)
func main() {
? ? var wg sync.WaitGroup
? ? wg.Add(1)
? ? go func() func(string) {
? ? ? ? fmt.Print("Go ") // evaluation order: 1
? ? ? ? return func(msg string) {
? ? ? ? ? ? fmt.Println("fun", msg) // evaluation order: 4
? ? ? ? ? ? wg.Done()
? ? ? ? }
? ? }()(func(msg string) string {
? ? ? ? fmt.Print("have ") // evaluation order: 2
? ? ? ? return msg
? ? }("with Go."))
? ? fmt.Print("some ") // evaluation order: ~ 3
? ? wg.Wait()
}
讓我用一個簡單的例子來解釋一下:
1. 考慮這個簡單的代碼:
i?:=?1 go?fmt.Println(i)?//?1
這很清楚:輸出是1
。
那么沒有人知道i;的值。您可以更改i代碼中的(請參閱下一個示例)
現在讓我們做這個閉包:
i := 1
go func() {
? ? time.Sleep(1 * time.Second)
? ? fmt.Println(i) // ?
}()
輸出確實是未知的,如果maingoroutine 早點退出,它甚至沒有機會運行:喚醒并打印i,它i本身可能會改變到那個特定時刻。
現在讓我們像這樣解決它:
i := 1
go func(i int) {?
? ? fmt.Printf("Step 3 i is: %d\n", i) // i = 1
}(i)
這個匿名函數參數是類型int并且是值類型,并且 的值i是已知的,并且編譯器生成的代碼將 value 1( i) 壓入堆棧,因此這個函數將使用 value 1,當時間到來時 (未來的某個時間)。
所有(圍棋游樂場):
package main
import (
? ? "fmt"
? ? "sync"
? ? "time"
)
func main() {
? ? i := 1
? ? go fmt.Println(i) // 1 (when = unknown)
? ? go fmt.Println(2) // 2 (when = unknown)
? ? go func() { // closure
? ? ? ? time.Sleep(1 * time.Second)
? ? ? ? fmt.Println(" This won't have a chance to run", i) // i = unknown? (when = unknown)
? ? }()
? ? i = 3
? ? wg := new(sync.WaitGroup)
? ? wg.Add(1)
? ? go func(i int) {
? ? ? ? defer wg.Done()
? ? ? ? fmt.Printf("Step 3 i is: %d\n", i) // i = 3 (when = unknown)
? ? }(i)
? ? i = 4
? ? go func(step int) { // closure
? ? ? ? fmt.Println(step, i) // i=? (when = unknown)
? ? }(5)
? ? i = 5
? ? fmt.Println(i) // i=5
? ? wg.Wait()
}
輸出:
5
5 5
2
1
Step 3 i is: 3
Go Playground 輸出:
5
5 5
1
2
Step 3 i is: 3
1您可能會注意到,和的順序2是隨機的,并且您的輸出可能會有所不同(請參閱代碼注釋)。
- 1 回答
- 0 關注
- 141 瀏覽
添加回答
舉報