为什么在主goroutine中评估input.Text() [英] why input.Text() is evaluated in the main goroutine

查看:69
本文介绍了为什么在主goroutine中评估input.Text()的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Go编程语言 ,对并发回显服务器的描述如下:

In chapter 8 of The Go Programming Language, there is a description to the concurrency echo server as below:


执行go语句本身时会评估go所启动函数的参数;因此,在主goroutine中对input.Text()进行了评估。

The arguments to the function started by go are evaluated when the go statement itself is executed; thus input.Text() is evaluated in the main goroutine.

我不明白这一点。为什么在主goroutine中评估 input.Text()

I don't understand this. Why the input.Text() is evaluated at the main goroutine? Shouldn't it be in the 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 main

import (
    "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)
    }
}

代码在这里: https:// github.com/adonovan/gopl.io/blob/master/ch8/reverb2/reverb.go

推荐答案

go 关键字在Go中的工作方式,请参见
go_statements

How go keyword works in Go, see Go_statements:


函数值参数是<在调用goroutine中像往常一样中对href = https://golang.org/ref/spec#Calls rel = nofollow noreferrer>进行评估,但是与常规调用不同,程序执行不会等待调用的函数完成。相反,该函数开始在新的goroutine中独立执行。当函数终止时,其goroutine也终止。如果函数有任何返回值,则在函数完成时将其丢弃。

The function value and parameters are evaluated as usual in the calling goroutine, but unlike with a regular call, program execution does not wait for the invoked function to complete. Instead, the function begins executing independently in a new goroutine. When the function terminates, its goroutine also terminates. If the function has any return values, they are discarded when the function completes.






使用 go 关键字对函数值参数进行评估(对于 defer 关键字请参见 defer 关键字)。


The function value and parameters are evaluated in place with the go keyword (same for the defer keyword see an example for defer keyword).

要了解评估顺序,请尝试以下操作:

To understand the evaluation order, let's try this:

go have()(fun("with Go."))

让我们运行,读取评估订单的代码注释:

Let's run this and read the code comments for the evaluation order:

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)返回funWithGo ,然后运行 fun( with Go。),结果为 fmt.Print( have)返回 with Go。;现在我们有 go funWithGo( with Go。)

Explanation go have()(fun("with Go.")):
First in place evaluation takes place here:
go have()(...) first have() part runs and the result is fmt.Print("Go ") and return funWithGo, then fun("with Go.") runs, and the result is fmt.Print("have ") and return "with Go."; now we have go funWithGo("with Go.").

所以最后一个 goroutine 调用是 go funWithGo( with Go。)

这是开始新的goroutine的调用,所以我们真的不知道它何时运行。这样就有可能使下一行运行: fmt.Print( some),然后我们在这里等待 wg.Wait()。现在,goroutine运行此 funWithGo( with Go。),结果为 fmt.Println( fun, with Go。) 然后 wg.Done();就是这样。

So the final goroutine call is go funWithGo("with Go.")
This is a call to start a new goroutine so really we don't know when it will run. So there is a chance for the next line to run: fmt.Print("some "), then we wait here wg.Wait(). Now the goroutine runs this funWithGo("with Go.") and the result is fmt.Println("fun", "with Go.") then wg.Done(); that is all.

让我们重写上面的代码,只用匿名函数替换命名函数,因此该代码与上面的代码相同:

例如参见:

Let's rewrite the above code, just replace named functions with anonymous one, so this code is same as above:
For example see:

func have() func(string) {
    fmt.Print("Go ") // evaluation order: 1
    return funWithGo
}

并剪切此代码,选择<在 go have()中有code> have 部分并粘贴,然后选择 have 放入 func have()中,然后按键盘上的 Delete ,您将得到: >
甚至更漂亮,结果相同,只需替换具有匿名功能的所有功能:

And cut this code select the have part in the go have() and paste then select the have part in func have() and press Delete on the keyboard, then you'll have this:
This is even more beautiful, with the same result, just replace all functions with anonymous functions:

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.考虑一下简单代码:


Let me explain it with a simple example:
1. Consider this simple code:

i := 1
go fmt.Println(i) // 1

这很清楚:输出为 1

但是,如果Go设计师决定在函数运行时中评估函数参数,那么没人会知道的值i ;您可以在代码中更改 i (请参见下一个示例)

But if the Go designers decided to evaluate the function argument at the function run-time nobody knows the value of i; you might change the i in your code (see the next example)


  1. 现在让我们进行以下关闭:



i := 1
go func() {
    time.Sleep(1 * time.Second)
    fmt.Println(i) // ?
}()

输出确实是未知,并且如果 main goroutine更快退出,它甚至没有机会运行:醒来并打印 i ,这本身就是 i 本身可能会更改为该特定时刻。

The output is really unknown, and if the main goroutine exits sooner, it even won't have a chance to run: Wake up and print the i, which is i itself may change to that specific moment.


  1. 现在让我们这样解决它:



i := 1
go func(i int) { 
    fmt.Printf("Step 3 i is: %d\n", i) // i = 1
}(i)

此匿名函数参数的类型为 int ,它是一个值类型,并且 i 的值是已知的,并且编译器生成的代码将值 1 i )到堆栈,因此此函数将使用值 1 ,时间到了(将来的某个时间)。

This anonymous function argument is of type int and it is a value type, and the value of i is known, and the compiler-generated code pushes the value 1 (i) to the stack, so this function, will use the value 1, when the time comes (A time in the future).


  1. 全部( The Go Playground ):

  1. All (The Go Playground):



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输出:

The Go Playground output:

5
5 5
1
2
Step 3 i is: 3

当您可能会注意到, 1 2 的顺序是随机的,并且您的输出可能会有所不同(请参见代码注释)

As you may be noticed, the order of 1 and 2 is random, and your output may differ (See the code comments).

这篇关于为什么在主goroutine中评估input.Text()的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆