Go 是一种轻量级的线程 由go关键字启动
和系统级线程的区别在于:
系统级线程都会有一个固定大小的栈(一般默认为2MB),这个栈主要用来存储函数递归时的参数和局部变量。导致在运行过程中存在两个问题:1、对于只需要很小的栈空间的线程是一个巨大的浪费;2、对于少量需要大量栈空间的线程又面临栈溢出的风险。
解决方案:1、降低固定栈大小,提升空间的利用率 2、增大栈的大小以允许更深的函数递归调用。
在golang中 一个goroutine会以一个很小的栈启动(可能是2KB或4KB),当遇到深度递归导致当前栈空间不足时,goroutine会根据需要动态的伸缩栈的大小(主流实现中栈的最大值可达到1GB)。由于goroutine启动的代价很小,所以我们可以轻易地启动成千上万个goroutine
go调度器可以在n个操作系统中多工调度m个协程。协程采用地是半抢占式的协作调度,只有在当前协程发生阻塞时 才会导致调度;同时调度发生在用户态,调度器根据具体函数只保存必要的寄存器,切换所使用的资源是轻量级的。
运行时通过指定runtime.GOMAXPROCS变量,用于控制当前运行正常非阻塞的协程的系统协程数目
执行go build value.go编译生成可执行文件value 执行./value即可调用程序
直接执行go run value.go不会编译,直接运行出结果
定义变量
package main
import (
"fmt"
)
func main() {
var v1 uint // 定义为无符号uint类型 默认为0
var x, y = 123, "hello" // 定义多个参数,根据=后值确定变量类型
u, t := 456, "world" //直接使用:=定义变量
fmt.Println(v1, x, y, u, t)
var c1 complex64 = 3 + 4i //定义复数
fmt.Println(c1)
}
定义常量
package main
import (
"fmt"
)
func main() {
const l int = 10 // 通过const关键字定义常量 定义常量规则 可指定常量类型
const w = 5 //直接赋值 不用指定变量类型
const a, b, c = 1, false, "01" //一次定义多个常量
fmt.Println(a, b, c)
area := l * w //定义变量
fmt.Println(area)
}
定义遍历迭代 ioat
ioat在每个const关键词内从0开始迭代递增
package main
import (
"fmt"
)
const (
a = iota + 1 //ioat=0
b //b是go中声明const时的缩略写法,表示值和它前面表达式等号右边的值是一样的,完整写法是 b=ioat+1 ioat=1
c = iota + 1 // ioat=2
d = iota //ioat=3
)
const (
e, f = iota + 1, iota + 2 //ioat=0
g, h //表示go语言的省略模式 值与前面表达式保持一致 ioat=1
i, j //go语言的省略写法 值与前面表达式保持一致 ioat=2
)
func main() {
fmt.Println(a, b, c, d)
fmt.Println(e, f, g, h, i, j)
}
go语言的指针
共有两种指针运算符,一种是取地址运算符 &,一种是间接寻址运算符 *。
package main
import (
"fmt"
)
func main() {
a := 10
var b *int = &a //b指向a的内存地址
fmt.Println(*b, b) //输出b内存地址的值
*b = 100//b寻址地址的值重新赋值
fmt.Println(*b, b, a)
}
1、 可以赋值成nil的变量类型有7种:1)任意类型的指针变量,2)函数变量,3)接口,4)error,5)map, 6)切片 7)通道。这些变量默认值就是nil,实际上都不用赋值成nil。
2、切片赋值时,最后一个元素后面如果有逗号则“}”可以换行或者不换行,否则”}“必须和最后一个元素保持在同一行。
3、数组切片新增元素 append追加另一个切片时需要加"…" 或者copy函数
4、Go目前只支持后置自增自减运算符,而且不能用于赋值或者作为参数传递给函数。
5、panic异常:空指针解析、下表越界、除数为0、调用panic函数
6、panic执行流程:
再正常流程中,调用recover异常捕获函数始终返回nil。
在异常流程中,出现 panic情况时,将会体哦那个之执行后续的普通语句。之前注册的defer函数仍然保证会被执行。
如果另起一个协程触发panic时,main函数里在协程之前注册的defer延迟函数不会被执行
如果当前defer函数是由之前的panic触发的并且当前defer函数会触发新的panic,则前一个panic停止执行
7、golang 中的类型比如:channel(通道)、complex(复数类型)、func(函数)均不能进行 JSON 格式化。
8、Gostub可以对全局变量、函数、过程打桩,可以打动态桩(对一个函数打桩后,多次调用函数会有不同的行为)
9、for循环遍历下的go协程的输出
1,golang是值拷贝传递;2,for循环很快就执行完了,但是创建的协程需要做初始化:上下文准备,堆栈,和内核态的线程映射关系的工作,是需要时间的,比for慢,等都准备好了的时候,会同时访问变量 。这个时候的变量肯定是for执行完成后的。所以协程都打印t最终的变量。解决方案为:闭包,给匿名函数增加入参,因为是值传递,所以每次for创建一个协程的时候,会拷贝一份变量传到这个协程里面去,这样就可以实现切片数组的打印了。修改后的代码和实验结果也在下面。
10 defer执行
1 执行在非函数环境下defer 关键字,作用和栈一样,先进后出
2 执行在函数环境下时 defer func(){}(),defer作用在函数返回前,函数内部赋值语句赋值后
3 defer 关键字同于函数的连续执行时,之后延迟最后一次函数的调用
如果想要连续函数的延迟 调用defer func(){}()函数
10 golang调用C代码的方式可以通过cgo或者是swig,而cgo是不能使用C++相关的东西的,比如标准库或者C++的面向对象特性。怎么办,将c++的功能函数封装成C接口,然后编译成动态库,或者是功能较为简单的可以直接嵌入到go源文件中
11 golang中根据首字母的大小写来确定可以访问的权限。无论是方法名、常量、变量名还是结构体的名称
如果首字母大写,则可以被其他的包访问;如果首字母小写,则只能在本包中使用
12 goto关键字,用来改变函数内代码的执行顺序,跳转到函数内指定的标签地方运行,goto不能跨函数代码块跳转
13 switch 语句后不需要加条件判断,支持多条件匹配,使用逗号分隔,例如:case val1,val2,val3。
不同的case之间不使用break分隔,默认自带break,只会执行一个case;
如果想要执行多个case,需要使用fallthrought关键字,且不会判断下一个case的表达式是否 为true;也可以使用break终止
14 go 的 := 赋值符初始化声明变量,它只能被用在函数体内,而不可以用于全局变量的声明与赋值
15 golang中大多数数据类型都可以转化为有效的JSON文本,除了channel、complex、函数等。在golang指针中可进行隐式转换,对指针取值,对所指对象进行序列化
16 在设计函数异常返回值的时候,如果函数异常值只有一个,则返回bool类型。如果异常值超过一个,则返回error。没有失败原因,则不返回bool或error。