【问题标题】:Go routine returns less results then the actual resultGo 例程返回的结果少于实际结果
【发布时间】:2021-10-28 01:22:05
【问题描述】:

我有一个循环,它对给定的键进行哈希处理并返回结果,但是在结果上,如果我有 1500 个进入循环的 URL 列表,它永远不会返回 1500 的结果,它总是返回小于1500.

我在下面做错了什么:

if len(URLLists) > 0 {
    var base = "https://example.com/query?="
    var wg sync.WaitGroup
    var mutex = sync.Mutex{}
    wg.Add(len(URLLists))
    for _, url := range URLLists {
        // wg.Add(1)  OR above for loop
        go func() {
            defer wg.Done()
            hmac := "HMAX_123"
            out := encoding.HexEncodeURL(hmac, url)
            final := base + out
            list := Lists{
                Old: url,
                New: final,
            }
            mutex.Lock()
            response.URL = append(response.URL, list)
            mutex.Unlock()
        }()
    }
    wg.Wait()
    jR, err := json.Marshal(response)
    if err != nil {
        w.Write([]byte(`{"success": false, "url" : ""}`))
    } else {
        w.Write(jR)
    }
    return
}

我为Add 尝试了这两种方法 - 一个内循环为 1,一个外循环为总长度。

我希望函数返回所有 1500 个 URL 列表,而不仅仅是“700、977、1123”随机列表。

看起来 - wg.Wait() 没有等待所有 wg.Add - 添加

【问题讨论】:

  • 如果在 go 例程之前添加 url:= url 会发生什么,或者更好的是,参数化 Go 例程
  • @DazWilkin - 解决了问题
  • @DazWilkin - 非常感谢您分享知识。 :)
  • 不客气!很高兴它有帮助

标签: go goroutine waitgroup


【解决方案1】:

这个程序有几个错误:

  1. 您正在 goroutine 中使用循环变量。循环变量在每次迭代时都会被重写,因此当 goroutine 使用 url 时,它可能已经移动到下一个 URL,因此您最终会得到多个 goroutine 散列相同的 URL。修复:
    for _, url := range URLLists {
        url:=url // Create a copy of the url
        // wg.Add(1)  OR above for loop
  1. 您有竞争条件。您必须保护对 response.URL 的访问,因为它是由多个 goroutine 编写的。您可以使用互斥锁:
lock:=sync.Mutex{}
for _,url:=...
  ...
  lock.Lock()
  response.URL = append(response.URL, list)
  lock.Unlock()

更好的方法是通过频道发送这些内容。

【讨论】:

  • 确实解决了问题,我在go func(url string) 中添加了url 并将其初始化}(url) 这是更好的方法吗?还是我按你的方式做? url:= url?
  • 两种方式都有效。重要的是复制循环变量,以便 goroutine 可以访问该副本,而不是更新的 url。
  • 谢谢!我将答案标记为完整:) 非常感谢您分享知识。
  • 关于您关于哪种方法更好的问题,将参数添加到 go 例程中会更清楚。说明该函数以url为值,避免了闭包的隐含性,更容易重构。例如,将无名/lambda 函数拉出、命名、测试(隔离)并在其他地方重用会更容易
【解决方案2】:

您的比赛状况非常严重:

response.URL = append(response.URL, list)

如果您要启动多达 1500 个并发 Go 例程,那么您将有数百个同时尝试执行这一行。他们将不断覆盖对数组的更改。

您需要使用sync.Mutex 保护将新数据插入到此切片中,或者通过通道发送结果并让单个 Go 例程从该通道读取并附加到列表中。

【讨论】:

  • 如果我在go 中使用mutex lock and unlock 进行插入,这不会减慢速度或与没有常规操作的同等效果吗?
  • 也许,也许不是?你必须测量,但这无关紧要。您需要将追加同步到该变量句点。如果您的程序产生不正确的结果,性能并不重要。
  • 我完全同意,非常感谢@meagar 的见解 - 我添加了互斥锁。
  • 正如在别处指出的那样,您还缺少url := url
  • 确实解决了问题,我在go func(url string) 中添加了url 并将其初始化}(url) 这是更好的方法吗?还是我应该这样做? url:= url?
猜你喜欢
  • 2019-09-26
  • 1970-01-01
  • 2019-11-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-19
  • 1970-01-01
  • 2023-01-19
相关资源
最近更新 更多