在go go频道上选择内部范围

时间:2017-09-09 17:10:33

标签: go goroutine

我跟随this post并行化了我的应用。我需要定制这段代码:

func sq(done <-chan struct{}, in <-chan int) <-chan int {
    out := make(chan int)
    go func() {
        defer close(out)
        for n := range in {
            select {
            case out <- n * n:
            case <-done:
                return
            }
        }
    }()
    return out
}

我不完全理解case out <- n * n:行。我可以看到,如果有n的值,那么将其平方并将其发送到频道,但我不明白为什么。 select仅采用第一个true案例吗?可以改写吗:

for n := range in {
    select {
    case n:
       out <- n * n
    case <-done:
       return
    }
}

无论如何,我需要用函数调用替换行case out <- n * n:。我已将其更改为以下内容:

out := make(chan structs.Ticket)

go func() {
    defer close(out)
    for url := range inputChannel {
        select {
        case url:
            data, err := GetData(url)
            fmt.Println("Got error: ", err)
            out <- data
        case <-done:
            return
        }
    }
}()

return out

看起来这将编译(我无法编译),但由于调试并行代码并不简单,我想检查使用case url是正确的方法选择range中的频道。这是对的吗?

更新

好的,我已经删除了我的代码中的剩余问题,现在当我尝试编译时,我收到错误消息:

url evaluated but not used
select case must be receive, send or assign recv

2 个答案:

答案 0 :(得分:0)

  

我不完全理解case out <- n * n行:我可以看到它说如果有n的值,那么将它平方并将其发送到频道,但我不明白为什么。

这不正确。 case out <- n * n检查out是否已准备好阅读,并n * n发送给out(如果是)。除非done也准备就绪。

当您有多个要与之通话的频道时,会使用

select。无论哪个频道准备就绪,它都会做到这一点。如果准备好多个频道,它将随机选择一个频道。

select {
    case out <- n * n:
    case <-done:
        return
    }
}

这会选择outdone。如果其中任何一个准备好继续,即。 out已准备好阅读或有done的内容,它会选择其中一个案例。订单是随机的,因此即使从out读取内容,也可以向done发送更多内容。

此模式用于关闭无限的goroutine。如果你停止从它的输出通道读取它,它将不再做任何工作,但它会在内存中徘徊。因此,通过将值传递给done,您可以告诉goroutine关闭。

UPDATE :在原始情况下,goroutine在输入通道上循环并发送输出,done是不必要的复杂功能。关闭输入通道后,该功能将返回。

func sq(in <-chan int) <-chan int {
    out := make(chan int)
    go func() {
        defer close(out)
        for n := range in {
            out <- n * n
        }
    }()
    return out
}

func main() {
    in := make(chan int)
    out := sq(in)
    for _,i := range []int{1,2,3,4} {
        in <- i
        fmt.Println(<-out)
    }

    // The `range` inside the goroutine from sq() will exit,
    // and the goroutine will return.
    close(in)
}

如果它只是吐出一组不断增加的正方形,那么在无限循环中就需要done

func sq(done chan bool) <-chan int {
    out := make(chan int)
    go func() {
        defer close(out)
        n := 0
        for {
            select {
                case <-done:
                    return
                case out<-n*n:
                    n++
            }
        }
    }()
    return out
}

func main() {
    done := make(chan bool)
    out := sq(done)
    for range []int{1,2,3,4} {
        fmt.Println(<-out)
    }

    // The switch in the goroutine will be able to read
    // from done (out's buffer being already full) and return.
    done <- true
}

答案 1 :(得分:0)

  1. 加入* { box-sizing: border-box; } body { padding: 0; margin: 0; background-color: #8799b7; overflow-y: scroll; max-height: 735px; } .header { display: block; background-image: url("header4.jpg"); height: 500px; } .home_page { display: flex; margin:0; padding: 0; } .home_page > div { border: 1px solid grey; } .home_left { height: 235px; width: 506px; margin: 0; padding: 0; } .home_center { height: 235px; width: 506px; margin: 0; padding: 0; } .home_right { height: 235px; width: 506px; margin: 0; padding: 0; } 或不会对range在此处所做的事情产生任何影响。

  2. 不,select没有采用第一个真正的表达式......它根本不采用表达式。唯一可以表达为表达式的东西是频道发送,频道接收和带右侧的频道接收分配。

    select
  3. 说“如果可以发送select { case out <- n * n: case <-done: return } (即它有剩余容量或有效阅读器),则将值out发送给它并继续。如果从n * n收到,则可能,从函数返回。如果两者都可以,那么随机选择一个并执行。如果两者都不可能,请等到其中一个成为可能。“ (请参阅规范中的Select Statements)。

    如果需要计算要发送的值(并且它太复杂而无法放在频道发送的右侧),只需在done之前执行即可。该规范清楚地表明,select中send语句中的所有表达式都是提前计算的,因此不会丢失任何内容。