Go 控制与并发
2020-07-10 22:09:21 0 举报
AI智能生成
Golang the good parts
作者其他创作
大纲/内容
流程控制
if
单if
if condition {
//do something
}
//do something
}
if SimpleStmt ; condition {
//do something
}
//do something
}
惯用法
if err := file.Chmod(0664); err != nil {
log.Print(err)
return err
}
log.Print(err)
return err
}
if-else
if condition {
//do something1
} else {
//do something2
}
//do something1
} else {
//do something2
}
推荐单if形式:
if condition {
//do something1
}
//do something2
//do something1
}
//do something2
串联if-else
if condition1 {
//do something
} else if condition2 {
//do something
} else {
//do something
}
//do something
} else if condition2 {
//do something
} else {
//do something
}
else-if的数量是没有限制的,但是为了代码的可读性,尽量少用else-if
for
基本形式
for 初始化语句; 条件语句; 修饰语句 {
}
}
for i := 0; i < 3; i++ {
fmt.Println("for inde x = ", i)
}
fmt.Println("for inde x = ", i)
}
条件形式
i := 5
for i >= 0 {
i--;
fmt.Println(i)
}
for i >= 0 {
i--;
fmt.Println(i)
}
go没有while,可用条件形式模拟
死循环
for {
//do something
}
//do something
}
for-break
结束整个循环体
for-continue
结束循环体的本次执行
continue只能用于for循环中
for-range
迭代器形式
配合string array slice map channel等数据结构一起使用
《Go编程语言规范》中的描述
四种形式
for i, v := range a {
...
}
...
}
for i := range a { // ignores value
...
}
...
}
for _, v := range a { // ignores index
...
}
...
}
for range a { // ignores index and value
...
}
...
}
switch
形式化定义
ExprSwitchStmt = "switch" [ SimpleStmt ";" ] [ Expression ] "{" { ExprCaseClause } "}" .
ExprCaseClause = ExprSwitchCase ":" StatementList .
ExprSwitchCase = "case" ExpressionList | "default" .
ExprCaseClause = ExprSwitchCase ":" StatementList .
ExprSwitchCase = "case" ExpressionList | "default" .
switch+语句+表达式
switch stmt;expr {... }
switch+表达式
switch expr {... }
单switch
switch {... }
能够模拟if-else,见例3
对应例子
case
可以在一个 case 中包含多个表达式,每个表达式用逗号分隔
default
可以放在 switch 语句的任何位置,但通常放在最后
fallthrough
用于标明执行完当前 case 语句之后,按顺序执行下一个case 语句
fallthrough 必须是 case 语句块中的最后一条语句。如果它出现在语句块的中间,编译器将会报错
不允许出现于type-switch
type-switch
跳转
goto + label
break(+ label)
可用于for switch select
continue(+ label)
只能用于for
例子
并发
channel
类型定义
ChannelType = ( "chan" | "chan" "<-" | "<-" "chan" ) ElementType .
三种类型
双向 chan T // 可以接收和发送类型为 T 的数据
只写 chan<- float64 // 只可以用来发送 float64 类型的数据
只读<-chan int // 只可以用来接收 int 类型的数据
只写 chan<- float64 // 只可以用来发送 float64 类型的数据
只读<-chan int // 只可以用来接收 int 类型的数据
make
无缓冲
make(chan int)
有缓冲
make(chan int, 100)
容量(capacity)代表Channel容纳的最多的元素的数量,代表Channel的缓存的大小。
如果没有设置容量,或者容量设置为0, 说明Channel没有缓存,只有sender和receiver都准备好了后它们的通讯(communication)才会发生(Blocking)。
如果设置了缓存,就有可能不发生阻塞, 只有buffer满了后 send才会阻塞, 而只有缓存空了后receive才会阻塞。一个nil channel不会通信。
如果没有设置容量,或者容量设置为0, 说明Channel没有缓存,只有sender和receiver都准备好了后它们的通讯(communication)才会发生(Blocking)。
如果设置了缓存,就有可能不发生阻塞, 只有buffer满了后 send才会阻塞, 而只有缓存空了后receive才会阻塞。一个nil channel不会通信。
close
ch 已关闭,ch<- 立即导致panic,不管ch是否满
ch 已关闭,<-ch 可以不断读取数据,读完继续读取则返回零值
ch 已关闭,for-range 可以不断读取数据,读完退出循环
c := make(chan int, 10)
c <- 1
c <- 2
close(c)
for i := range c {
fmt.Println(i)
}
c <- 1
c <- 2
close(c)
for i := range c {
fmt.Println(i)
}
i, ok := <-c
可以查看Channel的状态,判断值是零值还是正常读取的值
send
定义
SendStmt = Channel "<-" Expression .
Channel = Expression .
Channel = Expression .
ch<-10
规则
无缓存的channel只有在receiver准备好后send才被执行。如果有缓存,并且缓存未满,则send会被执行
往一个已经被close的channel中继续发送数据会导致run-time panic
往nil channel中发送数据会一致被阻塞着
recv
<-ch 用来从channel ch中接收数据,这个表达式会一直被block,直到有数据可以接收。
for-range
例子
c := make(chan int)
for i := range c {
fmt.Println(i)
}
for i := range c {
fmt.Println(i)
}
遍历channel直到为空,此时
如果channel被关闭则退出循环;
如果channel不关闭则一直阻塞
如果channel被关闭则退出循环;
如果channel不关闭则一直阻塞
参考资料
Go Channel 详解
由浅入深剖析 go channel
select
select就是用来监听和channel有关的收发操作,当数据可接收或可发送时触发相应的动作
select执行步骤
表达式求值
对所有出现在case语句中的通道表达式(elem <-ch 或 ch <- elem)求值, 得到所有的<接收通道>以及<发送通道+待发送值>
求值顺序:按代码书写顺序,即自上而下、从左到右
分支触发
就绪(随机选取)->default->阻塞
如果有一个或多个IO操作可以完成,则Go运行时系统会随机的选择一个执行,
否则的话,如果有default分支,则执行default分支语句,
如果连default都没有,则select语句会一直阻塞,直到至少有一个IO操作可以进行
否则的话,如果有default分支,则执行default分支语句,
如果连default都没有,则select语句会一直阻塞,直到至少有一个IO操作可以进行
分支执行
case
每个case操作一个channel,要么读,要么写
default
如果所有case都未就绪,则执行default
break
break之后的语句不在执行,直接跳出select
例子
《Go编程语言规范》中的例子
参考资料
定时器
ticker
ticker := time.NewTicker(1 * time.Second)
for range ticker.C {
...
}
for range ticker.C {
...
}
timer
timer := time.NewTimer(1 * time.Second)
select {
case <-timer.C:
fmt.Println("timeout")
}
select {
case <-timer.C:
fmt.Println("timeout")
}
after
select {
case <-time.After(3*time.Second ):
fmt.Println("timeout")
}
case <-time.After(3*time.Second ):
fmt.Println("timeout")
}
waitgroup
context
子主题
数据结构
主要函数
Background() Context
用于main函数、初始化、tests、或网络请求的根ctx
TODO() Context
无明确意图时使用todo ctx
WithCancel(parent Context) (ctx Context, cancel CancelFunc)
主动调用触发取消
WithDeadline(parent Context, d time.Time) (Context, CancelFunc)
时间触发取消,绝对时间
WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
时间触发取消,相对时间
WithValue(parent Context, key, val interface{}) Context
kv型ctx,用于传递数据
参考资料
深入理解Golang之Context
Golang 之context库用法
Go Concurrency Patterns: Context
反射
包
模块
0 条评论
下一页