李樂git
和以往認知的數組有很大不一樣。github
數組是值類型,賦值和傳參會複製整個數組;
數組長度必須是常量,且是類型的組成部分。[2]int 和 [3]int 是不一樣類型;
指針數組 [n]T(數組每一個元素都是指針),數組指針 [n]T(指向數組的指針);
內置函數 len 和 cap 都返回數組⻓長度 (元素數量)。算法
a := [3]int{1, 2} // 未初始化元素值爲 0。 b := [...]int{1, 2, 3, 4} // 經過初始化值肯定數組⻓長度。 d := [...]struct { name string age uint8 }{ {"user1", 10}, {"user2", 20}, // 別忘了最後一⾏的逗號。 } println(len(a), cap(a)) //3,3
slice並非數組或數組指針,它經過內部指針和相關屬性引用數組⽚片斷,以實現變長方案;slice是引用類型,但⾃自⾝身是結構體,值拷貝傳遞。定義以下:數組
struct Slice { // must not move anything byte* array; // actual data uintgo len; // number of elements uintgo cap; // allocated number of elements };
代碼舉例:app
data := [...]int{0, 1, 2, 3, 4, 5, 6} slice := data[1:4:5] // [low : high : max]
注意:這裏的max不能超過數組最大索引,不然編譯會報錯;讀寫操做實際目標是底層數組;函數
也可直接建立 slice 對象,會自動分配底層數組oop
s1 := []int{0, 1, 2, 3, 8: 100} // 經過初始化表達式構造,可以使⽤用索引號。 s2 := make([]int, 6, 8) // 使用 make 建立,指定 len 和 cap 值。 s3 := make([]int, 6) // 省略 cap,至關於 cap = len。
向 slice 尾部添加數據,返回新的 slice 對象,此時兩個slice底層公用同一個數組;可是一旦超出原 slice.cap 限制,就會從新分配底層數組。學習
s := make([]int, 0, 5) fmt.Printf("%p\n", &s) s2 := append(s, 1) fmt.Printf("%p\n", &s2) fmt.Println(s, s2) /* 0x210230000 0x210230040 [] [1] */
包內函數名稱首字母大寫時,能夠被其餘包訪問,不然不能;測試
函數調用:包名.函數名稱ui
方法調用:結構體變量或指針.函數名稱。
結構體:
type Node struct { id int data *byte next *Node }
函數:
func test(x, y int, s string) (int, string) { // 類型相同的相鄰參數可合併,(int,string)多返回值必須⽤用括號 n := x + y return n, fmt.Sprintf(s, n) } //變參 func test(s string, n ...int) string { var x int for _, i := range n { x += i } return fmt.Sprintf(s, x) } func main() { s := []int{1, 2, 3} println(test("sum: %d", s...)) //使用 slice 對象作變參時,必須展開 } //不能⽤用容器對象接收多返回值。只能⽤用多個變量,或 "_" 忽略 func test() (int, int) { return 1, 2 } func main() { // s := make([]int, 2) // s = test() // Error: multiple-value test() in single-value context x, _ := test() println(x) } //命名返回參數可看作與形參相似的局部變量,最後由 return 隱式返回 func add(x, y int) (z int) { z = x + y return }
方法(隸屬於結構體?):
type Data struct{ x int } func (self Data) ValueTest() { // func ValueTest(self Data); fmt.Printf("Value: %p\n", &self) } func (self *Data) PointerTest() { // func PointerTest(self *Data); fmt.Printf("Pointer: %p\n", self) } func main() { d := Data{} p := &d fmt.Printf("Data: %p\n", p) d.ValueTest() // ValueTest(d) d.PointerTest() // PointerTest(&d) p.ValueTest() // ValueTest(*p) p.PointerTest() // PointerTest(p) } /* Data : 0x2101ef018 Value : 0x2101ef028 Pointer: 0x2101ef018 Value : 0x2101ef030 Pointer: 0x2101ef018 */
https://github.com/CodisLabs/...
P是一個「邏輯Proccessor」,每一個G要想真正運行起來,首先須要被分配一個P(進入到P的local runq中,這裏暫忽略global runq那個環節)。對於G來講,P就是運行它的「CPU」,能夠說:G的眼裏只有P。但從Go scheduler視角來看,真正的「CPU」是M,只有將P和M綁定才能讓P的runq中G得以真實運行起來。
G-P-M模型的實現算是Go scheduler的一大進步,但Scheduler仍然有一個頭疼的問題,那就是不支持搶佔式調度,致使一旦某個G中出現死循環或永久循環的代碼邏輯,那麼G將永久佔用分配給它的P和M,位於同一個P中的其餘G將得不到調度,出現「餓死」的狀況。更爲嚴重的是,當只有一個P時(GOMAXPROCS=1)時,整個Go程序中的其餘G都將「餓死」。
Go 1.2中實現了「搶佔式」調度。這個搶佔式調度的原理則是在每一個函數或方法的入口,加上一段額外的代碼,讓runtime有機會檢查是否須要執行搶佔調度。這種解決方案只能說局部解決了「餓死」問題,對於沒有函數調用,純算法循環計算的G,scheduler依然沒法搶佔。
Go程序啓動時,runtime會去啓動一個名爲sysmon的m(通常稱爲監控線程),該m無需綁定p便可運行,該m在整個Go程序的運行過程當中相當重要:
若是G被阻塞在某個system call操做上,那麼不光G會阻塞,執行該G的M也會解綁P(實質是被sysmon搶走了),與G一塊兒進入sleep狀態。若是此時有idle的M,則P與其綁定繼續執行其餘G;若是沒有idle M,但仍然有其餘G要去執行,那麼就會建立一個新M。
package main import ( "fmt" "time" "runtime" ) func main() { runtime.GOMAXPROCS(48) for i:=0;i<10000;i++ { go func() { fmt.Println("start ", i) time.Sleep(time.Duration(10)*time.Second) }() } time.Sleep(time.Duration(2000)*time.Second) fmt.Println("main end") }
使用命令ps -eLf |grep test | wc -l查看程序線程數目。fmt.Println底層經過write系統調用向標準輸出寫數據。
1 | 5 |
---|---|
5 | 41 |
10 | 493 |
25 | 760 |
48 | 827 |
邏輯處理器的數目即程序最大可真正並行的G數目(小於機器核數),因此隨着邏輯處理器數目增長,並行向標準輸出寫數據阻塞時間越長,致使sysmon監控線程分離阻塞的M與P,同時建立新的M即線程。
package main import ( "fmt" "runtime" "time" ) func deadloop() { for { } } func main() { runtime.GOMAXPROCS(1) go deadloop() for { time.Sleep(time.Second * 1) fmt.Println("I got scheduled!") } }
測試結果:始終沒有輸出"I got scheduled!";由於只有一個邏輯處理器,且一直在執行協程deadloop,main協程沒法搶佔。
package main import ( "fmt" "runtime" "time" ) func add(a, b int) int { return a + b } func dummy() { add(3, 5) } func deadloop() { for { dummy() } } func main() { runtime.GOMAXPROCS(1) go deadloop() for { time.Sleep(time.Second * 1) fmt.Println("I got scheduled!") } }
測試結果:
root@nikel ~/gocoder$./deadloop I got scheduled! I got scheduled! I got scheduled! I got scheduled!
https://tonybai.com/2017/06/2...
https://tonybai.com/2017/11/2...
寫的挺好的,值得學習學習。