почему input.Text () оценивается в основной программе - PullRequest
0 голосов
/ 03 ноября 2019

В главе 8 из Язык программирования Go , есть описание для сервера параллельного эха, как показано ниже:

Аргументы функцииstart by go оценивается при выполнении самого оператора go;таким образом input.Text () вычисляется в главном порядке.

Я не понимаю этого. Почему input.Text() оценивается в главном рутине? Разве это не должно быть в goroutine go echo ()?

// 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

1 Ответ

0 голосов
/ 03 ноября 2019

Как работает ключевое слово go в Go, см. Go_statements :

Значение функции и Параметры равны оценивается как обычно в вызывающей программе, но в отличие от обычного вызова, выполнение программы не ожидает завершения вызванной функции. Вместо этого функция начинает выполняться независимо в новой процедуре. Когда функция завершается, ее процедура также завершается. Если у функции есть какие-либо возвращаемые значения, они сбрасываются после завершения функции.


* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 10 * * * *. на месте с ключевым словом go (то же самое для ключевого слова defer, см. Пример для ключевого слова defer ).


Чтобы понять порядок оценки,давайте попробуем это:

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

Давайте запустим this и прочитаем комментарии к коду для порядка оценки:

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.").

Итак, последний вызов программы - это go funWithGo("with Go.")
Это вызов для запуска новой программы, поэтому на самом деле мы не знаем, когда она запустится. Так что есть шанс для следующей строки: fmt.Print("some "), тогда мы ждем здесь wg.Wait(). Теперь программа запускает это funWithGo("with Go."), и результат будет fmt.Println("fun", "with Go.") затем wg.Done();это все.

Давайте перепишем приведенный выше код, просто заменим именованные функции на анонимные, так что этот код такой же, как указано выше:
Например, см .:

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

И вырезатьэтот код выберите have часть в go have() и вставьте, затем выберите have часть в func have() и нажмите Delete на клавиатуре, тогда у вас будет это:
This еще красивее, с тем же результатом, просто замените все функции анонимными функциями:

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.

Но если дизайнеры Go решили оценить аргумент функции на время выполнения функции , то никто не знает значение i;Вы можете изменить i в своем коде (см. следующий пример)


Теперь давайте сделаем это замыкание:
i := 1
go func() {
    time.Sleep(1 * time.Second)
    fmt.Println(i) // ?
}()

На самом деле вывод неизвестен , и если программа main завершится раньше, у нее даже не будетшанс на бег: проснись и напечатай i, что само по себе i может измениться к этому конкретному моменту.


Теперь давайте решим это так:
i := 1
go func(i int) { 
    fmt.Printf("Step 3 i is: %d\n", i) // i = 1
}(i)

Этот аргумент анонимной функции имеет тип int, это тип значения, а значение i известно, и сгенерированный компилятором код помещает значение 1 (i) в стек, поэтому эта функция будет использовать значение 1, когда придет время (время в будущем).


Все ( Игровая площадка Go ):
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 является случайным, и ваш вывод может отличаться (см. Комментарии к коду).

...