1. 一個已經被關閉的 channel 永遠都不會阻塞。當一個 channel 一旦被關閉,就不能再向這個 channel 發送數據,但仍然能夠嘗試從 channel 中獲取值。 golang
2. 已經被關閉的 channel 會實時返回。 緩存
package main import ( "fmt" "sync" "time" ) func main() { const n = 100000 finish := make(chan bool) var done sync.WaitGroup for 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 的任何細節而向它們發送信號而成爲可能,同時也不用擔憂死鎖。 app
當 close(finish) 依賴於關閉 channel 的消息機制,而沒有數據收發時,將 finish 定義爲 type chan struct{} 表示 channel 沒有任何數據;只對其關閉的特性感興趣。即:finish := make(chan struct{}) ide
3. 當 channel 的值還沒有進行初始化或賦值爲 nil 時,永遠都是阻塞的。
WaitMany() 中,一旦接收到一個值,就將 a 或 b 的引用設置爲 nil。當 nil channel 是 select 語句的一部分時,它實際上會被忽略,所以,將 a 設置爲 nil 便會將其從 select 中移除,僅僅留下 b 等待它被關閉,進而退出循環。 函數
4. slice append函數的各類技巧 優化
//添加切片 a = append(a, b, c, d) //將切片b添加至切片a a=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個位置插入元素x a = append(a[:i], append([]int{x}, a[i:]...)...) //在i個位置插入切片 a = append(a[:i], append([]int{x, y}, a[i:]...)...)
其中刪除操做就是覆蓋;而插入操做須要注意不能覆蓋掉插入位置及之後的元素。 編碼
5. 關於string與[]byte、[]rune相互轉換的問題: url
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 的緩存機制
spa
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 數據請求回來後 ungzip req.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.ReadCloser switch resp.Header.Get("Content-Encoding") { case "gzip": reader, _ = gzip.NewReader(resp.Body) defer reader.Close() default: reader = resp.Body }