go channel的一些技巧

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
}
相關文章
相關標籤/搜索