为了账号安全,请及时绑定邮箱和手机立即绑定

不生成相同的输出并发 Go 工作线程池

不生成相同的输出并发 Go 工作线程池

Go
素胚勾勒不出你 2022-10-04 19:33:16
我正在编写一个程序,该程序从文本文件中逐字逐字读取,以使用通道和工作线程池模式计算出现次数该程序在以程中工作:读取文本文件(函数)readTextreadText函数将每个单词发送到通道word每个戈鲁丁执行对地图中的单词进行计数的函数countWord每个戈鲁丁返回一个映射,工作器函数将结构的结果值传递给通道resultC测试函数根据来自通道的结果值创建映射resultC打印从步骤 5 创建的地图该程序有效,但当我尝试放置看到过程如下所示fmt.Println(0)func computeTotal() {    i := 0    for e := range resultC {        total[e.word] += e.count        i += 1        fmt.Println(i)    }}程序终止而不显示/计算所有单词1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 all goroutines finished 16 17 18 map[but:1 cat's:1 crouched:1 fur:1 he:2 imperturbable:1 it:1 pointed:1 sat:1 snow:1 stiffly:1 the:1 was:2 with:1] total words: 27 38 ... 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 Time taken for reading the book 5.8145ms如果我在此处的计算总计函数语句中取消注释 fmt.println(), 程序将正确显示结果,输出如下所示all goroutines finishedmap[a:83 about:4 above:2 absolute:1 accepted:1 across:1 affection:1 after:1 again:5  wonder:2 wood:5 wooded:1 woody:1 work:1 worked:2 world:4 would:11 wrapped:1 wrong:1 yellow:2 yielded:1 yielding:1 counts continues ......]total words:  856Time taken for reading the book 5.9924ms这是我对阅读文本的实现//ensure close words at the right timingfunc readText() {    file, err := os.Open(FILENAME)    if err != nil {        log.Fatal(err)    }    defer file.Close()    scanner := bufio.NewScanner(file)    scanner.Split(bufio.ScanWords)    for scanner.Scan() {        word := strings.ToLower(scanner.Text())        words <- strings.Trim(word, ".,:;")    }    //time.Sleep(1 * time.Second)    close(words)}这是我使用工人池的计数单词实现//call countWord func,func workerPool() {    var wg sync.WaitGroup    for i := 1; i <= NUMOFWORKER; i++ {        wg.Add(1)        go worker(&wg)    }    wg.Wait()    fmt.Println("all goroutines finished")    close(resultC)}我一直在寻找为什么程序没有显示一致的结果,但我还无法弄清楚。如何对程序进行更改,使其产生相同的结果?
查看完整描述

2 回答

?
一只甜甜圈

TA贡献1836条经验 获得超5个赞

该函数始终返回计数 == 1 的结果。countWord


下面是递增计数的函数版本:


func countWord(word string, tempMap map[string]int) Result {

    count := tempMap[word] + 1

    tempMap[word] = count

    return Result{word, count}

}

但要抱住这个想法!该函数假定结果为 1。鉴于问题中的工人总是按预期发送,我们可以通过直接从发送来将工人从图片中剔除。代码如下:computeTotalcountResult{word, 1}computeTotalResult{word, 1}readText


func computeTotal() {

    i := 0

    for e := range resultC {

        total[e.word] += e.count

        i += 1

        fmt.Println(i)

    }

}


func readText() {

    file, err := os.Open(FILENAME)

    if err != nil {

        log.Fatal(err)

    }

    defer file.Close()

    scanner := bufio.NewScanner(file)

    scanner.Split(bufio.ScanWords)


    for scanner.Scan() {

        word := strings.ToLower(scanner.Text())

        resultC <- Result{strings.Trim(word, ".,:;"), 1}

    }

    close(resultC)

}


main() {

    ...

    go readText()

    computeTotal()

    fmt.Println(total)

    ...

}

通道操作的开销可能否定了运行和单独戈鲁廷的任何好处。下面是组合成单个戈鲁廷的代码:computeTotalreadText


func main() {

    file, err := os.Open(FILENAME)

    if err != nil {

        log.Fatal(err)

    }

    defer file.Close()

    scanner := bufio.NewScanner(file)

    scanner.Split(bufio.ScanWords)


    var total = map[string]int{}

    for scanner.Scan() {

        word := strings.ToLower(strings.Trim(scanner.Text(), ".,:;"))

        total[word]++

    }

    fmt.Println(total)

}

问题中的函数使我认为您的目标是计算每个工人的单词并合并结果以获得总计。这是代码:countWord


func computeTotal() {

    for i := 1; i <= NUMOFWORKER; i++ {

        m := <-resultC

        for word, count := range m {

            total[word] += count

        }

    }

}


func workerPool() {

    for i := 1; i <= NUMOFWORKER; i++ {

        go worker()

    }

}


func worker() {

    var tempMap = make(map[string]int)

    for w := range words {

        tempMap[w]++

    }

    resultC <- tempMap

}


...

var resultC = make(chan map[string]int)

...


func main() {

    ...

    go readText()

    workerPool()

    computeTotal()

    ...

}


查看完整回答
反对 回复 2022-10-04
?
四季花海

TA贡献1811条经验 获得超5个赞

您必须通过以下方式重写函数:computeTotal


func computeTotal(done chan struct{}) {

    defer close(done)

    i := 0

    for e := range resultC {

        total[e.word] += e.count

        i += 1

        fmt.Println(i)

    }

}


func main() {


   computeTotalDone := make(chan struct{})

   go computeTotal(computeTotalDone)

   ...

   workerPool() //blocking

   <-computeTotalDone

   fmt.Println(total)

}

添加会导致无效结果的原因是您的实现具有争用条件。由于在主函数和函数中打印总结果并行运行,因此不能保证在调用之前处理所有消息。如果没有该功能,您的计算机上的速度就足以产生正确的结果。fmt.Printlnfmt.Println(total)computeTotalcomputeTotalfmt.Println(total)fmt.PrintlncomputeTotal


建议的解决方案可确保在调用之前完成。computeTotalfmt.Println(total)


查看完整回答
反对 回复 2022-10-04
  • 2 回答
  • 0 关注
  • 111 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信