6.并发编程之channel的遍历
(1).放入的比读取的少
分为两种情况: 是否关闭资源
1.没有关闭管道资源
package main import "fmt" var c = make(chan int) func main() { go func() { // 添加go 目的主要是为了同步 for i := 0; i < 2; i++ { c <- i } }() // 假如就写入两个那么读取三个会有什么样的结果? for i := 0; i < 3; i++ { r := <-c fmt.Printf("%v\t", r) } }
会产生死锁反应
2.关闭管道资源
package main import "fmt" var c = make(chan int) func main() { go func() { // 添加go 目的主要是为了同步 for i := 0; i < 2; i++ { c <- i } close(c) //--------------------关闭资源,会默认为类型的值 }() // 假如就写入两个那么读取三个会有什么样的结果? for i := 0; i < 3; i++ { r := <-c fmt.Printf("%v\t", r) } }
不会报错
(2).三种遍历方式
1.第一种for循环进行遍历
package main import "fmt" var c = make(chan int) func main() { go func() { // 添加go 目的主要是为了同步 for i := 0; i < 2; i++ { c <- i } close(c) //--------------------关闭资源,会默认为类型的值 }() // 假如就写入两个那么读取三个会有什么样的结果? for i := 0; i < 3; i++ { r := <-c fmt.Printf("%v\t", r) } }
2.range遍历
遍历管道的值的话,只有一个v值,没有下标
package main import "fmt" var c = make(chan int) func main() { go func() { // 添加go 目的主要是为了同步 for i := 0; i < 2; i++ { c <- i } close(c) //--------------------关闭资源,会默认为类型的值 }() // 假如就写入两个那么读取三个会有什么样的结果? for v := range c { // 只有一个 fmt.Printf("%v\t", v) } }
3.利用for if
package main import "fmt" var c = make(chan int) func main() { go func() { // 添加go 目的主要是为了同步 for i := 0; i < 2; i++ { c <- i } close(c) //--------------------关闭资源,会默认为类型的值 }() // 假如就写入两个那么读取三个会有什么样的结果? for true { v, ok := <-c // 这个是判断是否存在值,如果存在那么就为true if ok { fmt.Printf("%v\t", v) } else { break } } }
7.并发编程之select
(1).基础介绍
- select是Go中的一个控制层结构,类似于
switch
语句,用于处理异步IO操作。select
会监听case语句中channel的读写操作,当case中的channel读写操作为非阻塞状态(即能读写)时,将会触发相应的动作。
1.select 中的case语句必须是一个channel操作
2.select 中的default子句总是可以运行的
- 如果有多个case都可以运行,select会随机公平地选出一个执行,其他不会执行
- 如果没有可运行地 case 语句,且有 default 语句,那么就会执行 default地动作。
- 如果没有可运行地case语句,且没有default语句,select 将阻塞,直到某个case通信可以运行.
(2).select测试
package main import ( "fmt" "time" ) var chanInt = make(chan int) var chanString = make(chan string) func main() { go func() { //开启一个协助 chanInt <- 100 chanString <- "hello" defer close(chanString) defer close(chanInt) }() for true { select { case r := <-chanInt: fmt.Printf("%v\n", r) case r := <-chanString: fmt.Printf("%v\n", r) default: println("default....") } time.Sleep(time.Second) } }
输出结果不固定: 三个顺序不定。
2.假如没有default并且没有case 那么就会报死锁
前提是没有关闭资源,关闭资源就不会报死锁
package main import ( "fmt" "time" ) var chanInt = make(chan int) var chanString = make(chan string) func main() { go func() { chanInt <- 100 chanString <- "hello" //defer close(chanString) //defer close(chanInt) }() for true { select { case r := <-chanInt: fmt.Printf("%v\n", r) case r := <-chanString: fmt.Printf("%v\n", r) } time.Sleep(time.Second) } }
8.并发编程之Timer
(1).基础介绍
Timer顾名思义就是定时器的意思。可以实现一些定时操作,器内部也是通过channel来实现的。
1.第一种定义方式 time.NewTime() //进行创建 2.第二种定义方式 time.After() //进行创建
(2).定时器的实列
- 利用NerTime() 指定时间后再执行
package main import ( "fmt" "time" ) func main() { // 1.第一种定义定时器 timer := time.NewTimer(time.Second * 2) //设置指定时间后执行管道读出 fmt.Printf("现在的时间是: %v\n", time.Now()) t1 := <-timer.C //阻塞的,指定时间到了便会读出 fmt.Printf("管道读出的时间是: %v\n", t1) }
- 不设置变量名进行定时阻塞
package main import ( "fmt" "time" ) func main() { 1.第一种定义定时器 //timer := time.NewTimer(time.Second * 2) //设置指定时间后执行管道读出 //fmt.Printf("现在的时间是: %v\n", time.Now()) //t1 := <-timer.C //阻塞的,指定时间到了便会读出 //fmt.Printf("管道读出的时间是: %v\n", t1) fmt.Printf("现在的时间是: %v\n", time.Now()) timer := time.NewTimer(time.Second * 2) //设置指定时间后执行管道读出 <-timer.C //这里也会等待两秒钟 fmt.Printf("现在的时间是: %v\n", time.Now()) }
- After创建定时器
package main import ( "fmt" "time" ) func main() { 1.第一种定义定时器 //timer := time.NewTimer(time.Second * 2) //设置指定时间后执行管道读出 //fmt.Printf("现在的时间是: %v\n", time.Now()) //t1 := <-timer.C //阻塞的,指定时间到了便会读出 //fmt.Printf("管道读出的时间是: %v\n", t1) //fmt.Printf("现在的时间是: %v\n", time.Now()) //timer := time.NewTimer(time.Second * 2) //设置指定时间后执行管道读出 //<-timer.C //fmt.Printf("现在的时间是: %v\n", time.Now()) fmt.Printf("现在的时间是: %v\n", time.Now()) <-time.After(time.Second * 2) //利用After进行创建定时器 fmt.Printf("现在的时间是: %v\n", time.Now()) }