這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
1. 一個已經被關閉的 channel 永遠都不會阻塞。當一個 channel 一旦被關閉,就不能再向這個 channel 發送資料,但仍然可以嘗試從 channel 中擷取值。
2. 已經被關閉的 channel 會即時返回。
package mainimport ("fmt""sync""time")func main() {const n = 100000finish := make(chan bool)var done sync.WaitGroupfor i := 0; i < n; i++ {done.Add(1)go func() {select {case <-time.After(1 * time.Hour):case <-finish:}done.Done()}()}t0 := time.Now()close(finish)done.Wait()fmt.Printf("waited %v for %d goroutines to stop\n", time.Since(t0), n)}
當 finish channel 被關閉後,它會立刻返回。那麼所有等待接收 time.After channel 或 finish 的 goroutine 的 select 語句就立刻完成了,並且 goroutine 在調用 done.Done() 來減少 WaitGroup 計數器後退出。這個強大的機制在無需知道未知數量的 goroutine 的任何細節而向它們發送訊號而成為可能,同時也不用擔心死結。
當 close(finish) 依賴於關閉 channel 的訊息機制,而沒有資料收發時,將 finish 定義為 type chan struct{} 表示 channel 沒有任何資料;只對其關閉的特性感興趣。即:finish := make(chan struct{})
3. 當 channel 的值尚未進行初始化或賦值為 nil 時,永遠都是阻塞的。
WaitMany() 中,一旦接收到一個值,就將 a 或 b 的引用設定為 nil。當 nil channel 是 select 語句的一部分時,它實際上會被忽略,因此,將 a 設定為 nil 便會將其從 select 中移除,僅僅留下 b 等待它被關閉,進而退出迴圈。
4. slice append函數的各種技巧
//添加切片a = append(a, b, c, d)//將切片b添加至切片aa=append(a, b...)//複製切片b := make([]int, len(a))copy(b, a)//刪除指定位置元素[i:j]a = append(a[:i], a[j:]...)//刪除第n個元素a = append(a[:n], a[n+1:]...)//擴充n個空元素a = append(a, make([]int, n)...)//在第i個位置插入j個空元素a = append(a[:i], append(make([]int, j), a[i:]...)...)//在第i個位置插入元素xa = append(a[:i], append([]int{x}, a[i:]...)...)//在i個位置插入切片a = append(a[:i], append([]int{x, y}, a[i:]...)...)
其中刪除操作就是覆蓋;而插入操作需要注意不能覆蓋掉插入位置及以後的元素。
5. 關於string與[]byte、[]rune相互轉換的問題:
str := "hello世界" sli := []rune(str) []rune 是go內建的函數,會將字串按utf8編碼轉換為{h,e,l,l,o,世,界}對應的數字{104,101,108,108,111,19990,30028} byt := []byte(str) []byte 是go內建函數,會將str轉換為byte切片{104,101,108,108,111,228,184,150,231,149,140} for _,c := range str{ println(c) } len(str) 返回 11 len返回字串byte長度 go 中的字元可以是 ASCII/中文 .. s := '你' string(sli)/string(byt) 都返回 "hello世界" string()是go內建函數 無論是[]rune或者[]byte 都能通過string()函數返回相應的字串
6. 在使用多個 goroutine 列印內容時,經常因為使用 chan 不恰當而
導致主線程未等待其它 goroutine 全部執行完畢而匆匆推出,造成列印內容不全的問題,這裡對其中一種情況進行講解。
package main import ( "fmt" "runtime") // 從 1 至 1 億迴圈疊加,並列印結果。 func print(c chan bool, n int) { x := 0 for i := 1; i <= 100000000; i++ { x += i } fmt.Println(n, x) if n == 9 { c <- true } } func main() { // 使用多核運行程式 runtime.GOMAXPROCS(runtime.NumCPU()) c := make(chan bool) for i := 0; i < 10; i++ { go print(c, i) } <-c fmt.Println("DONE.") }
這段代碼從邏輯上看合乎情理,但是是一種非常
投機取巧 的做法,即根據第 10 個 goroutine 的執行情況來
草率地 認為前面的 9 個 goroutine 都已經執行完畢。如果你將 `runtime.GOMAXPROCS(runtime.NumCPU())` 這句注釋掉,使用單核運行程式,則將得到你所預期的效果;但如果使用多核的情況下,這種做法就是
錯誤的。
goroutine 是相互獨立的,且在執行過程中可能由於各種原因導致其中幾個 goroutine 讓出時間片給 CPU 去執行其它 goroutine。所以,我們
不能夠依靠 第 10 個 goroutine 的執行結果來判斷程式的運行情況。
解決方案一:利用 chan 的緩衝機制
package main import ( "fmt" "runtime") // 從 1 至 1 億迴圈疊加,並列印結果。 func print(c chan bool, n int) { x := 0 for i := 1; i <= 100000000; i++ { x += i } fmt.Println(n, x) c <- true} func main() { // 使用多核運行程式 runtime.GOMAXPROCS(runtime.NumCPU()) c := make(chan bool, 10) for i := 0; i < 10; i++ { go print(c, i) } for i := 0; i < 10; i++ { <-c } fmt.Println("DONE.") }
解決方案二:
使用 sync 包的 WaitGroup
package main import ( "fmt" "runtime" "sync") // 從 1 至 1 億迴圈疊加,並列印結果。 func print(wg *sync.WaitGroup, n int) { x := 0 for i := 1; i <= 100000000; i++ { x += i } fmt.Println(n, x) // 標識一次任務完成 wg.Done() } func main() { // 使用多核運行程式 runtime.GOMAXPROCS(runtime.NumCPU()) // 建立等待組 wg := sync.WaitGroup{} // 設定需要等待的對象個數 wg.Add(10) for i := 0; i < 10; i++ { go print(&wg, i) } // 等待所有任務完成 wg.Wait() fmt.Println("DONE.") }
7. golang http請求最佳化
//判斷url是否有效//沒有http://開頭,就加上http://if !strings.HasPrefix(feed, "http") { feed = "http://" + feed}//判斷url是否合理host, err := url.ParseRequestURI(feed)if err != nil {}//判斷是否能解析到對應的host記錄_, err = net.LookupIP(host.Host)if err != nil {}//向主機請求資料client := &http.Client{ Transport: &http.Transport{ Dial: func(netw, addr string) (net.Conn, error) { deadline := time.Now().Add(10 * time.Second) c, err := net.DialTimeout(netw, addr, 5*time.Second) //連線逾時時間 if err != nil { return nil, err } c.SetDeadline(deadline) return c, nil }, },}req, err := http.NewRequest("GET", feed, nil)if err != nil {}//資料轉送壓縮//告訴主機 支援gzip 資料請求回來後 ungzipreq.Header.Set("User-Agent", "Mozilla/5.0 (compatible; UJCspider/0.1; +http://ujiecao.com/help)")req.Header.Set("Accept-Encoding", "gzip")resp, err := client.Do(req)if err != nil {}defer resp.Body.Close()var reader io.ReadCloserswitch resp.Header.Get("Content-Encoding") {case "gzip": reader, _ = gzip.NewReader(resp.Body) defer reader.Close()default: reader = resp.Body}