
- IO模型
- PCB
- socket
- gorountine调度
- defer
- 1先进后出
- 2遇到panic ,先defer后panic
- 3 defer语句包含函数的 先执行函数 再defer
- 4 defer和return
- select
- context
- 继承/方法
- go fun( )闭包
- 1.变量值未被修改:
- 2变量值被修改
- make和new初始化
- iota
- 结构体比较
- append多个元素
- 短变量声明
- 常量
- 自定义类型和别名
- 结构体和结构体别名
- 变量作用域
func Defer(){
for i:=0;i<5;i++{
fmt.Println(i)
}
}
defer : 4
defer : 3
defer : 2
defer : 1
defer : 0
先进后出
2遇到panic ,先defer后panicfunc defer_call() {
defer func() { fmt.Println("打印前") }()
defer func() { fmt.Println("打印中") }()
defer func() { fmt.Println("打印后") }()
panic("触发异常")
}
打印后
打印中
打印前
panic: 触发异常
3 defer语句包含函数的 先执行函数 再defer
4 defer和return
func main() {
fmt.Println("user1",DeferFunc1(1))
fmt.Println("user2",DeferFunc2(1))
fmt.Println("user3",DeferFunc3(1))
}
func DeferFunc1(i int) (t int) {
t = i
defer func() {
fmt.Println("d1",t)
t += 3
}()
fmt.Println("r1",t)
return t
}
func DeferFunc2(i int) int {
t := i
defer func() {
fmt.Println("d2",t)
t += 3
}()
fmt.Println("r2",t)
return t
}
func DeferFunc3(i int) (t int) {
defer func() {
fmt.Println("d3",t)
t += i
}()
fmt.Println("r3",t)
return 2
}
执行顺序
r1 1
d1 1
user1 4
r2 1
d2 1
user2 1
r3 0
d3 2
user3 3
select
配合case用于channel
1.Go的select语句是一种仅能用于channl发送和接收消息的专用语句,此语句运行期间是阻塞的;当select中没有case语句的时候,会阻塞当前groutine。
2.select是Golang在语言层面提供的I/O多路复用的机制,其专门用来检测多个channel是否准备完毕:可读或可写。
3.select语句中除default外,每个case *** 作一个channel,要么读要么写
4.select语句中除default外,各case执行顺序是随机的
5.select语句中如果没有default语句,则会阻塞等待任一case
6.select语句中读 *** 作要判断是否成功读取,关闭的channel也可以读取 作者:地鼠文档 https://www.bilibili.com/read/cv10128860/ 出处:bilibili
contextContext 的结构非常简单,它是一个接口。
Context 提供跨越API的截止时间获取,取消信号,以及请求范围值的功能。//
它的这些方案在多个 goroutine 中使用是安全的
type Context interface {
// 如果设置了截止时间,这个方法ok会是true,并返回设置的截止时间
Deadline() (deadline time.Time, ok bool)
// 如果 Context 超时或者主动取消返回一个关闭的channel,如果返回的是nil,表示这个
// context 永远不会关闭,比如:Background()
Done() <-chan struct{}
// 返回发生的错误
Err() error
// 它的作用就是传值
Value(key interface{}) interface{}
}
写到这里,我们打住想一想,如果你来实现这样一个能力的 package,你抽象的接口是否也是具备这样四个能力?
获取截止时间
获取信号
获取信号产生的对应错误信息
传值专用
闭包捕获对外部变量是通过引用的方式实现的;会随着外部变量的改变而修改。为避免此问题可:
通过参数方式传入外部变量;
定义局部变量的方式;
func delayPrint() {
// 通过参数方式保证每个变量值是不同的;
for i := 0; i < 3; i++ {
go func(i int) {
time.Sleep(time.Second * 1)
fmt.Println("By param: ", i)
}(i)
}
time.Sleep(time.Second * 4)
// 直接引用外部变量,会发现所有调用最终都捕获了同一个变量值
for i := 0; i < 3; i++ {
go func() {
time.Sleep(time.Second * 1)
fmt.Println("By clouser: ", i)
}()
}
time.Sleep(time.Second * 4)
// 通过引入局部变量方式,保证捕获的变量是不同的
for i := 0; i < 3; i++ {
tmp := i
go func() {
time.Sleep(time.Second * 1)
fmt.Println("By tmp: ", tmp)
}()
}
time.Sleep(time.Second * 4)
}
// By param: 2
// By param: 0
// By param: 1
// By clouser: 3
// By clouser: 3
// By clouser: 3
// By tmp: 0
// By tmp: 2
// By tmp: 1
————————————————
1.变量值未被修改:
捕获变量c对应两个地址
闭包函数对应一个地址
f
1
在
栈
上
存
一
个
地
址
a
d
d
r
2
,
a
d
d
r
2
存
储
两
个
值
,
捕
获
变
量
c
和
函
数
入
口
地
址
a
d
d
r
1
\color{#A0A}{f1在栈上存一个地址addr2 ,addr2存储两个值 , 捕获变量c和函数入口地址addr1}
f1在栈上存一个地址addr2,addr2存储两个值,捕获变量c和函数入口地址addr1
f
2
在
栈
上
存
一
个
地
址
a
d
d
r
3
,
a
d
d
r
3
存
储
两
个
值
,
捕
获
变
量
c
和
函
数
入
口
地
址
a
d
d
r
1
\color{#A0A}{f2在栈上存一个地址addr3 , addr3存储两个值 , 捕获变量c和函数入口地址addr1}
f2在栈上存一个地址addr3,addr3存储两个值,捕获变量c和函数入口地址addr1
1两次循环闭包的捕获变量存储的都是地址,create执行完i的值是2,这个地址写入了返回值
2返回值拷贝给fs的时候,这个捕获变量i地址指向的值就是2
3调用fs[i]的时候,addr0,addr1一次写入寄存器,两个闭包捕获变量地址指向同一个位置,值是2
new:
1 iota配合const使用
2 行数的索引,从0开始,在第几行iota就是几
3 常亮没有赋值的,上一行是iota,+1;上一行是int,=上一行的值
定义在同一行就是同一个index
sn1 := struct {
age int
name string
}{age: 11, name: "qq"}
sn2 := struct {
age int
name string
}{age: 11, name: "qq"}
if sn1 == sn2 {
fmt.Println("sn1 == sn2")
}
结构体只有在属性相同 类型相同 顺序相同才可以==,否则panuc
append多个元素 短变量声明var(
size =1024
max_size = size*2
)
func main() {
size2:=65536
fmt.Println(size,max_size,size2)
}
只能在func内部,全局变量不能用:=
常量
常量不能取地址
//一、类型别名 用=
type byte=uint8
type rune=int32
//二、区别类型别名和类型定义
// 自定义类型myInt,基本类型是int
type myInt int
//将 int 类型取一个别名intAlias
type intAlias = int
func main() {
var a myInt //声明 a变量为自定义 myInt 类型
fmt.Printf("a Type: %T, value: %d\n", a, a) // 输出 a 的类型和默认值
var b intAlias //声明 b变量为 intAlias 类型
fmt.Printf("b Type: %T, value: %d\n", b, b) // 输出 b 的类型和默认值
}
a Type: main.myInt, value: 0
b Type: int, value: 0
//从上面的结果我们可以看出:
//a 的类型是 main.myInt,表示main 包下定义的myInt 类型
//b 的类型是 int 。intAlias 类型只会在代码中存在,编译完成时,不会有 intAlias 类型``
```go
结构体和结构体别名
变量作用域
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)