Go 通道中缺少数据

Go 通道中缺少数据,go,concurrency,channel,goroutine,Go,Concurrency,Channel,Goroutine,我写了一个小程序来练习go频道 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)

我写了一个小程序来练习go频道

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)
}
因此,&{start 0}未打印。为什么缺少这个?

因为在SERVICE中,循环变量用于在单独的goroutine上执行的函数文本中,该函数文本由运行循环的goroutine同时修改:data race。如果存在数据竞争,则行为未定义

如果您复制变量,它将工作:

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

另一种可能是将其作为参数传递给匿名函数:

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

以下几个相关问题对此进行了详细说明:

正如Zan Lynx所指出的,您的主goroutine不会等待所有启动的goroutine完成,因此您可能看不到所有打印的请求。查看此问题如何等待已启动的goroutine:

,因为在服务中,循环变量在单独goroutine上执行的函数文本中使用,该函数文本由运行循环的goroutine:data race同时修改。如果存在数据竞争,则行为未定义

如果您复制变量,它将工作:

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

另一种可能是将其作为参数传递给匿名函数:

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

以下几个相关问题对此进行了详细说明:


正如Zan Lynx所指出的,您的主goroutine不会等待所有启动的goroutine完成,因此您可能看不到所有打印的请求。查看此问题如何等待已启动的goroutines:

这是因为当匿名执行程序时,请求总是从请求{0}移动到请求{1},所以从{start 1}打印


这是因为当匿名执行程序执行时,req总是从请求{0}移动到请求{1},所以从{start 1}打印


你和sem的关系很奇怪。如果你想在一个频道上拥有有限数量的读卡器,那么只需要启动那么多的读卡器。每个读者都会阅读需求。呃,随便了。你的sem和我读过的其他Go代码不一样。无论如何,我会这样写,而不是你必须这样做:@ZanLynx谢谢你的额外建议!你和sem的关系很奇怪。如果你想在一个频道上拥有有限数量的读卡器,那么只需要启动那么多的读卡器。每个读者都会阅读需求。呃,随便了。你的sem和我读过的其他Go代码不一样。无论如何,我会这样写,而不是你必须这样做:@ZanLynx谢谢你的额外建议!我注意到您的代码和原始代码不能执行9,因为它没有等待足够长的时间来完成goroutines。@ZanLynx是的,您是对的,我只关注有问题的问题。我意识到这些错误也在有效的go文章中讨论过。然而,读完这篇文章后,我仍然不明白第二个建议是如何工作的。也就是说,req是一个指针,for循环仍在改变它,这与我的代码中的情况相同。唯一的区别是req作为闭包中的参数传递。您能详细说明一下它是如何工作的吗?@drdot在详细的地方添加了3个相关问题的链接。我注意到您的和原始代码不能执行9,因为它没有等待足够长的时间让goroutines完成。@ZanLynx是的,您是对的,我只是把重点放在问题上。我意识到这些错误也会在有效的围棋文章中讨论。然而,读完这篇文章后,我仍然不明白第二个建议是如何工作的。也就是说,req是一个指针,for循环仍在改变它,这与我的代码中的情况相同。唯一的区别是req作为闭包中的参数传递。你能详细说明一下它是如何工作的吗?@drdot在相关问题中添加了3个链接,其中有详细说明。