Недостающие данные в канале - PullRequest
0 голосов
/ 03 декабря 2018

Я написал небольшую программу для практики го канала.

package main

import (
    "log"
    "strconv"
)

var MaxOutstanding int = 1
var channelSize int = 10
var sem = make(chan int, MaxOutstanding)

type Request struct {
    command string
    data    string
}

func process(req *Request) {
    log.Println(req)
}

func serve(reqs chan *Request) {
    for req := range reqs {
        sem <- 1
        go func() {
            process(req)
            <-sem
        }()
    }
}

func main() {
    reqs := make(chan *Request, channelSize)
    for i := 0; i < channelSize; i++ {
        req := &Request{"start", strconv.Itoa(i)}
        reqs <- req
    }
    close(reqs)
    serve(reqs)
}

При печати

2018/12/02 16:52:30 &{start 1}
2018/12/02 16:52:30 &{start 2}
2018/12/02 16:52:30 &{start 3}
2018/12/02 16:52:30 &{start 4}
2018/12/02 16:52:30 &{start 5}
2018/12/02 16:52:30 &{start 6}
2018/12/02 16:52:30 &{start 7}
2018/12/02 16:52:30 &{start 8}
2018/12/02 16:52:30 &{start 9}

Поэтому & {start 0} не печатается.Почему этого не хватает?

Ответы [ 2 ]

0 голосов
/ 03 декабря 2018

Это происходит из-за того, что при анонимном выполнении требование уже переместилось с Request{0} на Request{1}, поэтому вы печатаете с {start 1}.

// method 1: add a parameter, pass it to anonymous function
func serve(reqs chan *Request) {
    for req := range reqs {
        sem <- 1

        // You should make the req as parameter of this function
        // or, when the function execute, the req point to Request{1}
        go func(dup *Request) {
            process(dup)
            <-sem
        }(req)
    }

    // And you should wait for the Request{9} to be processed
    time.Sleep(time.Second)
}

// method 2: make for wait anonymous function
func serve(reqs chan *Request) {
    for req := range reqs {
        go func() {
            process(req)
            sem <- 1
        }()

        // Or wait here
        <-sem
    }
}
0 голосов
/ 03 декабря 2018

Поскольку в serve() ваша переменная цикла используется внутри литерала функции, который вы выполняете в отдельной программе, которая одновременно изменяется программой, выполняющей цикл: гонка данных.Если у вас гонка данных, поведение не определено.

Если вы сделаете копию переменной, она будет работать:

for req := range reqs {
    sem <- 1
    req2 := req
    go func() {
        process(req2)
        <-sem
    }()
}

Попробуйте на Go Playground .

Еще одна возможность - передать это анонимной функции в качестве параметра:

for req := range reqs {
    sem <- 1
    go func(req *Request) {
        process(req)
        <-sem
    }(req)
}

Попробуйте это на Go Playground .

Это подробно описано в нескольких смежных вопросах:

Использование указателей в цикле for - Golang

Golang: регистрация нескольких маршрутов с использованием диапазона для фрагментов цикла / карты

Почему эти два варианта цикла дают мне другое поведение?

Кроме того, как отметил Зан Линкс, ваша основная программа не ждет всех запущенных программ длязавершено, поэтому вы можете не увидеть все напечатанные запросы.Посмотрите на этот вопрос, как вы можете ждать запуска подпрограмм: Предотвращение завершения функции main () до завершения выполнения подпрограмм в Голанге

...