【发布时间】:2020-08-02 15:55:55
【问题描述】:
我是 Go 新手,想知道一些我无法弄清楚的非常基本的问题。
只是为了锻炼(真正需要的抽象),我需要:
- 用常量
ITERATIONS固定的一些元素初始化字符串切片 - 遍历这个切片并为每个元素运行一个 goroutine
- 每个 goroutine 将花费一定的时间来处理元素(随机的第二个持续时间)
- 工作完成后,我希望 goroutine 将结果推送到通道
- 然后我需要从这个通道(在从主 goroutine 调用的函数中)捕获所有结果,将它们附加到最终切片,然后,一旦完成
- 打印最终切片的长度并添加一些基本时间跟踪
下面是一个有效的代码。
毫不奇怪,程序的总时间总是或多或少等于 const MAX_SEC_SLEEP 的值,因为所有处理 goroutine 都是并行工作的。
但是呢:
- 接收部分:
我真的需要将我的 select 语句包装在一个 for 循环中,迭代确切数量的 ITERATIONS ,以获得与将结束通道的 goroutine 数量完全相同的接收器数量吗?这是避免死锁的唯一方法吗?如果由于某种原因,其中一个 goroutine 失败了怎么办?
我找不到一种方法来让一个简单的 for (ever) 循环包装选择,有两种情况(一种从结果通道接收,另一种像 case <-done 这样将从函数返回)。会是更好的模式吗?
或者迭代通道并检测它是否从某个地方关闭会更好?
- 发送部分
在所有迭代之后,我应该在某个地方关闭频道吗?但我肯定会在至少一个 gouroutine 完成之前关闭它,以恐慌错误结束(试图发送到关闭的频道)
如果我要插入done <- true 模式,它会在这里吗?
- 等待组
我并没有真正尝试过等待组,我需要一种方法来捕获来自 goroutine 的所有“返回”值并将它们附加到最终切片;除了使用通道之外,我没有找到从 goroutine 返回的正确方法。
- 杂项
我应该在 func 参数中传递通道还是让它们按原样对程序全局化?
- (错误的)代码
package main
import (
"fmt"
"log"
"math/rand"
"time"
)
const ITERATIONS = 200
var (
results chan string
initial []string
formatted []string
)
func main() {
defer timeTrack(time.Now(), "program")
format() //run format goroutines
receive() //receive formatted strings
log.Printf("final slice contains %d/%d elements", len(formatted), len(initial))
}
//gets all results from channel and appends them to formatted slice
func receive() {
for i := 0; i < ITERATIONS; i++ {
select {
case result := <-results:
formatted = append(formatted, result)
}
}
}
//loops over initial slice and runs a goroutine per element
//that does some formatting operation and then pushes result to channel
func format() {
for i := 0; i < ITERATIONS; i++ {
go func(i int) {
//simulate some formatting code that can take a while
sleep := time.Duration(rand.Intn(10)) * time.Second
time.Sleep(sleep)
//append formatted string to result chan
results <- fmt.Sprintf("%s formatted", initial[i])
}(i)
}
}
//initialize chans and inital slice
func init() {
results = make(chan string, ITERATIONS)
for i := 0; i < ITERATIONS; i++ {
initial = append(initial, fmt.Sprintf("string #%d", i))
}
}
func timeTrack(start time.Time, name string) {
elapsed := time.Since(start)
log.Printf("%s took %s", name, elapsed)
}
【问题讨论】:
标签: go concurrency goroutine channels