李乐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...
写的挺好的,值得学习学习。