当前位置: 首页 > 知识库问答 >
问题:

Goroutine、渠道和僵局

向锦
2023-03-14

我试图更多地了解Go的通道和goroutine,所以我决定制作一个小程序,从bufio.newscanner对象读取的文件中计算单词:

nCPUs := flag.Int("cpu", 2, "number of CPUs to use")
flag.Parse()
runtime.GOMAXPROCS(*nCPUs)    

scanner := bufio.NewScanner(file)
lines := make(chan string)
results := make(chan int)

for i := 0; i < *nCPUs; i++ {
    go func() {
        for line := range lines {
            fmt.Printf("%s\n", line)
            results <- len(strings.Split(line, " "))
        }
    }()
}

for scanner.Scan(){
    lines <- scanner.Text()
}
close(lines)


acc := 0
for i := range results {
      acc += i
 }

fmt.Printf("%d\n", acc)

现在,在我发现的大多数示例中,结果通道都将被缓冲,比如make(chan int,NUMBER_OF_LINES_IN_FILE)。但是,在运行这段代码之后,我的程序仍然存在一个致命错误:所有goroutines都是睡眠-死锁!错误消息。

基本上,我认为我需要两个通道:一个通道将文件中的行与goroutine通信(因为它可以是任意大小的,所以我不认为我需要在make(chan)函数调用中通知大小);另一个通道将从goroutine收集结果,并在main函数中使用它来计算累积结果。

用goroutines和channels以这种方式编程的最佳选择应该是什么?任何帮助都是非常感谢的。

共有1个答案

张勇
2023-03-14

正如@Andrewn所指出的,问题是每个goroutine都试图发送到results通道,但是这些发送将会阻塞,因为results通道没有缓冲,并且在for i:=range results循环之前不会从它们中读取任何内容。您永远不会到达那个循环,因为首先需要完成scanner.scan()循环,该循环试图将所有发送到通道,该通道被阻塞,因为goroutines永远不会循环回范围行,因为它们无法发送到结果

解决这个问题的第一件事是将scanner.scan()放在goroutine中,这样就可以立即读取results通道。但是,您将面临的下一个问题是知道何时结束i:=range results循环的。您希望关闭results通道,但只有在原始goroutines读取lines通道之后。您可以在关闭通道后立即关闭Results通道,但是我认为这可能会引入潜在的竞争,因此最安全的做法也是在关闭Results通道之前等待原始的两个goroutines完成:(playground链接):

package main

import "fmt"
import "runtime"
import "bufio"
import "strings"
import "sync"

func main() {
    runtime.GOMAXPROCS(2)

    scanner := bufio.NewScanner(strings.NewReader(`
hi mom
hi dad
hi sister
goodbye`))
    lines := make(chan string)
    results := make(chan int)

    wg := sync.WaitGroup{}
    for i := 0; i < 2; i++ {
        wg.Add(1)
        go func() {
            for line := range lines {
                fmt.Printf("%s\n", line)
                results <- len(strings.Split(line, " "))
            }
            wg.Done()
        }()
    }

    go func() {
        for scanner.Scan() {
            lines <- scanner.Text()
        }
        close(lines)
        wg.Wait()
        close(results)
    }()

    acc := 0
    for i := range results {
        acc += i
    }

    fmt.Printf("%d\n", acc)
}

 类似资料:
  • 渠道分析 渠道来源细分

  • 问题内容: 想象以下代码: 这会泄漏通道和goroutine,还是Go识别出已消失而goroutine可以退出? 如果通道的缓冲区大小为2,答案是否会有所不同? 问题答案: 如果通道未缓冲,则匿名函数之一将不会返回。该程序泄漏了goroutine和通道。 如果通道的缓冲区大小大于或等于1,则两个匿名函数都将返回。goroutines和channel使用的资源将被回收。 缓冲区大小为1足以防止泄漏。

  • 我是新来的,想知道一些很基本的问题,我不能弄清楚。 为了发挥作用(对实际需要的抽象),我需要: 用常数固定的元素初始化字符串片断 遍历此切片并为每个元素运行一个goroutine 每个goroutine将花费一定的时间来处理元素(随机秒持续时间) 作业完成后,我希望goroutine将结果推送到一个通道 然后我需要捕获来自这个通道的所有结果(在一个从主goroutine调用的函数中),将它们附加到

  • 国内市场上有许许多多的应用市场,常见的有:百度、360、腾讯应用宝、豌豆荚等。 其他手机厂家如小米、华为、魅族、三星等都有自己的应用市场,总共有上百家! 问题 发版前,Android工程师打包了上百个渠道版本,如何检验渠道号与apk名称是否一致? 希望自动去获取apk的友盟渠道号,并自动进行校验。 怎么做 Android Apk的渠道号一般存放在AndroidManifest.xml文件中。 批量

  • 1. 简介 “转化归因-渠道归因”报告能够帮助您洞察消费者在与您的产品接触过程中的每个广告触点对最终转化带来的价值。您需要结合投放目标,选择合适的归因模型进行分析。 渠道归因报告能够帮助您洞察这些问题: · 本轮广告投放,哪个渠道的拉新效果最好 · 辅助转化的渠道都有哪些 2. 使用简介 在使用“渠道转化归因”相关报告前,您需要将业务上有价值的事件(如下单、注册、留资等)标记为转化。您可以在“管理

  • 功能介绍 获取渠道转化漏斗的漏斗图和转化图数据。 接口 https://openapi.baidu.com/rest/2.0/mtj/svc/app/getDataByKey 请求参数 此处仅列本接口特有参数,公共参数请参考报告级API说明 获取表格数据 参数名 参数类型 是否必须 描述 method string 是 conversion/channelconversion/a;convers