【GoLang】golang 閉包 closure 參數傳遞的蹊蹺!

來源:互聯網
上載者:User

標籤:code   sample   開始   如何   com   閉包   default   聲明   index   

結論:

閉包函數可以直接引用外層代碼定義的變數,

但是,注意,閉包函數裡面引用的是變數的地址,

當goroutine被調度時,改地址的值才會被傳遞給goroutine 函數。

 

介紹

go的閉包是一個很有用的東西。但是如果你不瞭解閉包是如何工作的,那麼他也會給你帶來一堆的bug。這裡我會拿出Go In Action這本書的一部分代碼,來說一說在使用閉包的時候可能遇到的坑。全部的代碼在github上。

 

閉包的坑

首先看一段代碼:

search/search.go29  // 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     }3637     // 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.go37     // 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變數的值在作為參數傳入匿名函數閉包的時候也就同時被鎖定。在使用閉包訪問外部變數的時候,問問你自己這個變數時候會發生改變,這樣的改變對閉包的運行有什麼影響。

 

 

參考資料:

http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/index.html#closure_for_it_vars

http://studygolang.com/articles/7994

http://studygolang.com/articles/4738

http://blog.csdn.net/htyu_0203_39/article/details/50985187

http://studygolang.com/articles/356

http://www.jb51.net/article/74166.htm

http://www.cnblogs.com/yjf512/archive/2012/12/09/2810313.html

https://www.zhihu.com/question/49341044?from=profile_question_card

http://www.jb51.net/article/81813.htm

 

【GoLang】golang 閉包 closure 參數傳遞的蹊蹺!

相關文章

Cloud Intelligence Leading the Digital Future

Alibaba Cloud ACtivate Online Conference, Nov. 20th & 21st, 2019 (UTC+08)

Register Now >

Starter Package

SSD Cloud server and data transfer for only $2.50 a month

Get Started >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。