亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

為什么在主 goroutine 中計算 input.Text()

為什么在主 goroutine 中計算 input.Text()

Go
忽然笑 2023-07-31 16:59:33
在《Go編程語言》第8章中,對并發回顯服務器的描述如下:go 啟動的函數的參數在 go 語句本身執行時進行評估;因此 input.Text() 在主 goroutine 中進行計算。我不明白這一點。為什么要input.Text()在主 goroutine 上求值?它不應該在 go echo() goroutine 中嗎?// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.// License: https://creativecommons.org/licenses/by-nc-sa/4.0/// See page 224.// Reverb2 is a TCP server that simulates an echo.package mainimport (? ? "bufio"? ? "fmt"? ? "log"? ? "net"? ? "strings"? ? "time")func echo(c net.Conn, shout string, delay time.Duration) {? ? fmt.Fprintln(c, "\t", strings.ToUpper(shout))? ? time.Sleep(delay)? ? fmt.Fprintln(c, "\t", shout)? ? time.Sleep(delay)? ? fmt.Fprintln(c, "\t", strings.ToLower(shout))}//!+func handleConn(c net.Conn) {? ? input := bufio.NewScanner(c)? ? for input.Scan() {? ? ? ? go echo(c, input.Text(), 1*time.Second)? ? }? ? // NOTE: ignoring potential errors from input.Err()? ? c.Close()}//!-func main() {? ? l, err := net.Listen("tcp", "localhost:8000")? ? if err != nil {? ? ? ? log.Fatal(err)? ? }? ? for {? ? ? ? conn, err := l.Accept()? ? ? ? if err != nil {? ? ? ? ? ? log.Print(err) // e.g., connection aborted? ? ? ? ? ? continue? ? ? ? }? ? ? ? go handleConn(conn)? ? }}
查看完整描述

1 回答

?
慕碼人2483693

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是隨機的,并且您的輸出可能會有所不同(請參閱代碼注釋)。


查看完整回答
反對 回復 2023-07-31
  • 1 回答
  • 0 關注
  • 141 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號