【问题标题】:Finite loop in Go routineGo 例程中的有限循环
【发布时间】:2017-11-13 16:28:44
【问题描述】:

我正在尝试在完成主题后同步执行两个或多个 goroutine

goroutines order of execution

我编写了一些代码来测试 goroutine 的行为 (我把它当作一个队列,顺便说一句,go 并发教程页面对我没有帮助)

package main

import (
        "fmt"
        "sync"
)

const size = 10
var vChan chan int = make(chan int, size)

func justSend(wg *sync.WaitGroup) {
        defer wg.Done()
        for i := 1; i <= 10; i++ {
                vChan <- i
        }
}

func justPrint(wg *sync.WaitGroup) {
        defer wg.Done()
        v := <- vChan
        fmt.Printf("%4d ", v);
}

func main() {
        wg := new(sync.WaitGroup)

        go func() {
                wg.Add(1)
                go justSend(wg)
                wg.Wait()
                wg.Add(size)
                go justPrint(wg)
                wg.Wait()
                close(vChan)
        }()
}

但是它什么也没打印,我们是否应该设计一个 goroutine 总是在那里有一个无限循环,或者我们不能让它正常工作?

(这段代码是为我在构建多个 websocket 连接和测试我们的 API 服务器响应方面的实际问题准备的,所以......我认为在不同的函数之间传递一堆具有相同类型的通道非常烦人,所以我设置了全局)

【问题讨论】:

  • 您的应用程序在您有机会获得任何输出之前退出。 go func() { ... })() 语句完成后 - 没有任何东西可以阻止程序退出。
  • 我不确定你想要完成什么,但我提供这个:play.golang.org/p/GGx5HGOtBL
  • 感谢@CeriseLimón 操场的支持,但这是否意味着我需要先关闭通道,然后才能在另一个 go 例程中从中读取值?
  • wg.Add(size) wg.Done 的例程。但是,main 在例程完成之前返回。试试go build -race 看看比赛检测器是否能发现你错过的一些事情,然后使用delve 来逐步检查所有内容

标签: go


【解决方案1】:

不,不要使用无限循环。 同步最好通过通道或同步包的工具来完成。main() 也是一个 goroutine(主要的),所以如果 main 在其他 goroutine 之前退出,您的任务仍未完成。 一种方法是使用 sync.WaitGroup 等待其他 goroutine 完成(参见示例 1,2 和 3)。另一种方法是使用通道等待其他 goroutine 完成(参见示例 4 和 5)。我提供了一些示例,以便您可以选择适合您情况的示例:


1- 一个 tx 和多个 rx(保持通道打开),试试this

package main

import (
    "fmt"
    "sync"
)

func main() {
    go tx()
    wg.Add(20)
    for i := 1; i <= 20; i++ {
        go rx()
    }
    wg.Wait()
}

func tx() {
    for i := 1; i <= 20; i++ {
        vChan <- i
    }
}

func rx() {
    defer wg.Done()
    fmt.Println(<-vChan)
}

var wg sync.WaitGroup

var vChan = make(chan int, 10)

2- 一个 tx 和一个 rx(保持通道打开),试试this

package main

import (
    "fmt"
    "sync"
)

func main() {
    go tx()
    wg.Add(1)
    go rx()
    wg.Wait()
}

func tx() {
    for i := 1; i <= 20; i++ {
        vChan <- i
    }
}

func rx() {
    defer wg.Done()
    for i := 1; i <= 20; i++ {
        fmt.Println(<-vChan)
    }
}

var wg sync.WaitGroup

var vChan = make(chan int, 10)

3- 关闭通道以发送信号结束,尝试this

package main

import (
    "fmt"
    "sync"
)

func main() {
    go tx()
    wg.Add(1)
    go rx()
    wg.Wait()
}

func tx() {
    for i := 1; i <= 5; i++ {
        vChan <- i
    }
    close(vChan)
}

func rx() {
    defer wg.Done()
    for v := range vChan {
        fmt.Println(v)
    }
}

var wg sync.WaitGroup

var vChan = make(chan int, 10)

4- 关闭通道到 tx 的信号结束,使用主 goroutine 进行 rx,尝试this

package main

import (
    "fmt"
)

func main() {
    go tx()
    for v := range vChan {
        fmt.Println(v)
    }
}

func tx() {
    for i := 1; i <= 20; i++ {
        vChan <- i
    }
    close(vChan)
}

var vChan = make(chan int, 10)

5- 使用退出频道,尝试this

package main

import (
    "fmt"
)

func main() {
    go tx()
    go rx()
    <-quit
}
func rx() {
    for v := range vChan {
        fmt.Println(v)
    }
    quit <- struct{}{}
}
func tx() {
    for i := 10; i <= 15; i++ {
        vChan <- i
    }
    close(vChan)
}

var quit = make(chan struct{}, 1)
var vChan = make(chan int, 10)

输出:

10
11
12
13
14
15

6- Multi tx multi rx,试试this

package main

import (
    "fmt"
    "sync"
)

func main() {
    wg.Add(5)
    for i := 1; i <= 5; i++ {
        go tx()
        go rx()
    }
    wg.Wait()
}

var n = 10

func tx() {
    vChan <- n
    n++
}

func rx() {
    defer wg.Done()
    fmt.Println(<-vChan)
}

var wg sync.WaitGroup

var vChan = make(chan int, 10)

7- 多发一发,试试this

package main

import (
    "fmt"
    "sync"
)

func main() {
    for i := 1; i <= 5; i++ {
        wg.Add(1)
        go tx()
    }

    go rx()
    wg.Wait()
}

func rx() {
    for {
        fmt.Println(<-vChan)
        wg.Done()
    }
}

var n = 20

func tx() {
    vChan <- n
    n++
}

var wg sync.WaitGroup

var vChan = make(chan int, 10)

我希望这会有所帮助。

【讨论】:

    【解决方案2】:

    您已将 goroutine 放入 mainmain 函数在您的 go 例程完成之前完成。所以要么不要在main 中使用goroutine,要么在main 的末尾使用某种布尔接收通道,当goroutine 完成时会得到true

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-12-08
      • 2017-06-29
      • 1970-01-01
      • 2017-05-18
      • 2018-08-05
      • 2014-04-06
      • 1970-01-01
      相关资源
      最近更新 更多