zoukankan      html  css  js  c++  java
  • golang闭包里的坑

    介绍

    go的闭包是一个很有用的东西。但是如果你不了解闭包是如何工作的,那么他也会给你带来一堆的bug。这里我会拿出Go In Action这本书的一部分代码,来说一说在使用闭包的时候可能遇到的坑。全部的代码在github上。

    闭包的坑

    首先看一段代码:

    search/search.go
    
    29  // Launch a goroutine for each feed to find the results.
    30  for _, feed := range feeds {
    31     // Retrieve a matcher for the search.
    32     matcher, exists := matchers[feed.Type]
    33     if !exists {
    34        matcher = matchers["default"]
    35     }
    36
    37     // Launch the goroutine to perform the search.
    38     go func(matcher Matcher, feed *Feed) {
    39        Match(matcher, feed, searchTerm, results)
    40        waitGroup.Done()
    41     }(matcher, feed)
    42  }

    这段代码从30行开始遍历一个Feed的slice。在for range语句中声明的feed变量的值在每一个循环中都不同。之后从32行的代码在检查一个某个特定的key值是否有值,如果不存在则赋一个默认值。和feed变量一样,matcher的值也是每个循环都不一样。

    现在我们可以跳到38行到41行。这几行代码显然还是在for range循环中的。这里我们定义了一个匿名函数,并把这个函数做为一个goroutine运行。这个匿名函数接受两个参数,第一个是Matcher类型的值,第二个是一个Feed类型的指针。在地41行,我们可以蛋刀matcher和feed两个变量被传入了匿名函数中。

    这个匿名函数在第39行的实现很有意思。这里我们可以看到一个对Match方法的调用。这个方法接受4个参数,如果你仔细看的话,前两个参数就是我们定义匿名函数声明的而两个参数。后面的两个我们没有在匿名函数中声明。而是作为变量直接在匿名函数使用了。

    search/search.go
    
    37     // Launch the goroutine to perform the search.
    38     go func(matcher Matcher, feed *Feed) {
    39        Match(matcher, feed, searchTerm, results)
    40        waitGroup.Done()
    41     }(matcher, feed)
    42  }

    变量searchTerm和results是定义在闭包外部的。我们可以在匿名函数内部直接使用,而不必作为参数传入后再使用。这里就会有一个问题:我们为什么要把变量matcher和feed作为参数传入而其他的两个不是呢?

    我在一开始就指出,matcher和feed两个变量的值是如何在每一个for range循环中改变的。searchTerm和results的值不会随着循环而改变,他们的值在每一个goroutine的生命周期中都是常量。当然,这个goroutine就是使用的匿名函数。那么,为什么要这么做呢?

    当我们在匿名函数闭包中使用一个变量的时候,我们不必在匿名函数声明的时候作为参数传递。这个匿名函数闭包可以直接访问到定义在其外部的变量,也就是说对这个变量的修改会在匿名函数闭包内部体现出来,也就是这里的goroutine。如果我们把matcher和feed变量这样使用,而不是把他们作为参数传入匿名函数闭包。那么多数情况下gotoutine只会处理for range循环的最后一个值。

    在这个例子中,所有的goroutine都会并发执行。for range循环也许在第一个最多第二个goroutine还在运行的时候就运行完了,matcher和feed变量只会有最后一次循环时候的值。也就是说即使不是全部的goroutine也是大部分的goroutine会处理这些变量的相同的值。这种情况适用于searchTerm和results变量,因为他们不会在循环中改变值。

    结论

    幸好我们可以声明可以接收参数的匿名函数,这些类型的闭包问题也就引刃而解。在我们上面的例子中,当每一个匿名函数都声明在for range的作用域内的时候,matcher和feed变量的值在作为参数传入匿名函数闭包的时候也就同时被锁定。在使用闭包访问外部变量的时候,问问你自己这个变量时候会发生改变,这样的改变对闭包的运行有什么影响。

  • 相关阅读:
    Luogu 1080 【NOIP2012】国王游戏 (贪心,高精度)
    Luogu 1314 【NOIP2011】聪明的质检员 (二分)
    Luogu 1315 【NOIP2011】观光公交 (贪心)
    Luogu 1312 【NOIP2011】玛雅游戏 (搜索)
    Luogu 1525 【NOIP2010】关押罪犯 (贪心,并查集)
    Luogu 1514 引水入城 (搜索,动态规划)
    UVA 1394 And Then There Was One / Gym 101415A And Then There Was One / UVAlive 3882 And Then There Was One / POJ 3517 And Then There Was One / Aizu 1275 And Then There Was One (动态规划,思维题)
    Luogu 1437 [HNOI2004]敲砖块 (动态规划)
    Luogu 1941 【NOIP2014】飞扬的小鸟 (动态规划)
    HDU 1176 免费馅饼 (动态规划)
  • 原文地址:https://www.cnblogs.com/sunshine-anycall/p/4771149.html
Copyright © 2011-2022 走看看