[go]并发编程(go关键字和select关键字)
go关键字 - 协程(异步)管理结构
其实和if switch for等循环结构一样. go 是一种控制结构, 控制协程的启动等生命周期.
并发基础
程序与进程
并发与并行
启动多个协程
// 同步代码
func hello() {
time.Sleep(time.Second/3)
fmt.Println("Hello Goroutine!")
}
func main() {
hello()
fmt.Println("main goroutine done!")
}
//异步
func hello() {
time.Sleep(time.Second/3)
fmt.Println("Hello Goroutine!")
}
func main() {
go hello() // 启动另外一个goroutine去执行hello函数
fmt.Println("main goroutine done!") //不会被输出
}
//main结束,程序即运行结束
func main() {
go hello() // 启动另外一个goroutine去执行hello函数
fmt.Println("main goroutine done!")
time.Sleep(time.Second)
}
- 协程同步
- runtime包里的函数
// runtime.GOMAXPROCS() 设置核心数
- 用一个core执行
func main() {
runtime.GOMAXPROCS(1)
go func() {
for {
fmt.Print(1)
}
}()
go func() {
for {
fmt.Print(2)
}
}()
time.Sleep(time.Second * 1)
}
//打印的1 2 成块的
- 用2个core执行
func main() {
runtime.GOMAXPROCS(2)
go func() {
for {
fmt.Print(1)
}
}()
go func() {
for {
fmt.Print(2)
}
}()
time.Sleep(time.Second * 1)
}
//打印的1 2交替较明显
// runtime.Gosched() //出让当前执行机会, 等下次又机会时继续往下执行
func main() {
runtime.GOMAXPROCS(1)
go func() {
for {
fmt.Print("m")
}
}()
go func() {
for {
runtime.Gosched()
fmt.Print("g")
}
}()
time.Sleep(time.Second)
}
- runtime.Goexit()结束本go程
- return结束当前函数
// return结束当前函数
func test() {
println(1)
return
println(2)
}
func main() {
test()
println("main")
}
//1
//main
//runtime.Goexit结束当前go程
func test() {
println(1)
runtime.Goexit()
println(2)
}
func main() {
test()
println("main")
}
// 1
// fatal error: no goroutines (main called runtime.Goexit) - deadlock!
// defer: 当函数结束时被调用
func main() {
defer println("test end 1") //栈结构:一个桶 后进先出
defer println("test end 2")
println("testing")
}
//testing
//test end 2
//test end 1
// return后的defer不会被注册
func main() {
defer println("1")
println("main")
return
defer println("2")
}
//main
//1
// runtime.Goexit后的defer不会被注册
func main() {
defer println("1")
println("main")
runtime.Goexit()
defer println("2")
}
//main
//1
//fatal error: no goroutines (main called runtime.Goexit) - deadlock!
- 小结:
runtime.Goexit()和return后的defer都不会被注册,
只有代码能执行到,defer才会被注册
func main() {
go func() {
defer fmt.Println("aaaaaaaaaaaaa")
runtime.Goexit()
defer fmt.Println("bbbbbbbbbb")
}()
time.Sleep(time.Second * 1)
}
//aaaaaaaaaaaaa
func main() {
go func() {
defer fmt.Println("aaaaaaaaaaaaa")
return
defer fmt.Println("bbbbbbbbbb")
}()
time.Sleep(time.Second * 1)
}
//aaaaaaaaaaaaa
// 带go
func test() {
defer fmt.Println("ccccccccccc")
runtime.Goexit()
defer fmt.Println("dddddddddddddd")
}
func main() {
go func() {
defer fmt.Println("aaaaaaaaaaaaa")
go test()
defer fmt.Println("bbbbbbbbbb")
}()
time.Sleep(time.Second)
}
//bbbbbbbbbb
//aaaaaaaaaaaaa
//ccccccccccc
//不带go
func test() {
defer fmt.Println("ccccccccccc")
runtime.Goexit()
defer fmt.Println("dddddddddddddd")
}
func main() {
go func() {
defer fmt.Println("aaaaaaaaaaaaa")
test()
defer fmt.Println("bbbbbbbbbb")
}()
time.Sleep(time.Second)
}
//ccccccccccc
//aaaaaaaaaaaaa
定时器
睡一秒
//方法1:
func main() {
time.Sleep(time.Second)
}
//方法2:
func main() {
fmt.Println(<-time.NewTimer(time.Second).C))
}
//方法3:
func main() {
fmt.Println(<-time.After(time.Second))
}
// 定时回调(setTimeout)
time.AfterFunc(time.Second, func() {
println("after 1s cb")
})
time.Sleep(time.Second*3)
//周期定时器(setInterval)
func main() {
tickTimer := time.NewTicker(time.Second)
for {
fmt.Println(<-tickTimer.C)
}
}
select - 管理多个chan
select是一种控制结构, 和if switch for控制结构一样, 用于控制管理多个管道
select可以同时监控多个通道的情况,只处理未阻塞的case。
每个case必须是一个chan的io操作
select会阻塞在多个channel上,对多个channel的读/写事件进行监控。
case中的channel的事件包括:
读取的时候,
channel被close,或
写入时channel没有空间。
// 多个chan,随机选一个执行. 对多个chanel调度机会基本是均等的
当通道为nil时,对应的case永远为阻塞,无论读写。
特殊关注:普通情况下,对nil的通道写操作是要panic的。
func main() {
c1 := make(chan interface{}); close(c1)
c2 := make(chan interface{}); close(c2)
c3 := make(chan interface{}); close(c3)
var c1Count, c2Count, c3Count int
for i := 1000; i >= 0; i-- {
select {
case <-c1:
c1Count++
case <-c2:
c2Count++
case <-c3:
c3Count++
}
}
fmt.Printf("c1Count: %d\nc2Count: %d\nc3Count: %d\n", c1Count, c2Count, c3Count)
}
//select管理多个管道
func main() {
ch := make(chan int, 1) //仅限于缓冲通道
for i := 0; i < 10; i++ {
select {
case x := <-ch:
fmt.Println(x)
case ch <- i:
}
}
}
// select管理多个管道
func main() {
ch := make(chan int)
done := make(chan bool)
go func() {
for i := 0; i < 10; i++ {
ch <- i
time.Sleep(time.Second / 3)
}
close(ch)
done <- true
}()
for {
select {
case <-done:
fmt.Println("Done!")
return
case t := <-ch:
fmt.Println("ch value: ", t)
}
}
}
// 没有任何channel准备好,处理默认事件
func main() {
start := time.Now()
var c1, c2 <-chan int
select {
case <-c1:
case <-c2:
default:
fmt.Printf("In default after %v\n\n", time.Since(start))
}
}
// 没有任何channel准备好,处理超时
func main() {
var c <-chan int
for {
select {
case <-c:
case <-time.After(1 * time.Second):
fmt.Println("Timed out.Do something.")
}
}
}
2. 退出
select {
case <- done:
cleanUp()
return
default:
}
3.判断channel是否阻塞
func main() {
var ch chan int = make(chan int, 5)
for {
select {
case ch <- 1:
fmt.Println("add success")
default: // channel满了
fmt.Println("chan is full")
return
}
}
}
4.检测chanel关闭事件
func main() {
start := time.Now()
c := make(chan interface{})
go func() {
time.Sleep(2*time.Second)
close(c)
}()
fmt.Println("Blocking on read...")
select {
case <-c:
fmt.Printf("Unblocked %v later.\n", time.Since(start))
}
}
// select{} 永久阻塞, 直到有信号中断.
func main() {
go func() {
for {
fmt.Println(time.Now())
time.Sleep(time.Second / 3)
}
}()
select {}
}