channel
channel不同的翻译资料叫法不一样,常见的叫法:管道,信道,通道,channel是进程内的通信方式,每个channel只能传递一个类型的值,这个类型需要在声明channel时指定
channel在Golang中主要有2个作用:同步、通信。
var 名称 chan 类型 // 读写var 名称 chan <- 类型 // 只写var 名称 <- chan 类型 // 只读名称 := make(chan int) // 无缓存channel名称 := make(chan int, 0) // 无缓存channel名称 := make(chan int, 100) // 有缓存channel
案例
package mainimport ("fmt""time")func main() {// channel// demo44_1()// demo44_2()// 协程之间的通信// demo44_3()// 数据很多,用range接收// demo44_4()// channel是安全的,多个goroutine同时操作时,同一时间只能由一个goroutine存取数据demo44_5()}func demo44_5() {ch := make(chan int)for i := 1; i < 5; i++ {go func(j int) {fmt.Println(j, "开始")ch <- jfmt.Println(j, "结束")}(i)}for j := 1; j < 5; j++ {time.Sleep(2 * time.Second)num := <-chfmt.Println("取出数据:", num)}}func demo44_4() {ch1 := make(chan string)ch2 := make(chan int)go func() {for i := 97; i < 97+26; i++ {ch1 <- fmt.Sprintf("%c", i)}ch2 <- 1}()go func() {// 代码是阻塞的(range)for c := range ch1 {fmt.Printf("取出:%s \\n", c)}}()// 同步等待<-ch2// time.Sleep(1e9)fmt.Println("demo44_4() end")}// 子协程之间的同步func demo44_3() {ch1 := make(chan string)ch2 := make(chan int)go func() {ch1 <- "传送数据"ch2 <- 1}()go func() {s1 := <-ch1fmt.Println("取出数据:", s1)ch2 <- 1}()// 同步等待<-ch2<-ch2// time.Sleep(1e9)fmt.Println("demo44_3() end")}// 主协程和子协程之间的同步func demo44_2() {ch := make(chan int)go func() {fmt.Println("执行")//fmt.Println("结束")ch <- 999}()a := <-chfmt.Println(a)fmt.Println("demo44_2() end")}func demo44_1() {ch := make(chan int)go func() {fmt.Println("进入 goroutine")close(ch)}()c, d := <-chfmt.Println(c, d)fmt.Println("demo44_1() end.")}
死锁和有缓存通道死锁
goroutine中向无缓存channel添加内容或在主goroutine中向channel添加内容,且添加内容的个数已经大于channel缓存个数就会产生死锁。
死锁,在城中多个进程由于相互竞争资源而产生的阻塞(等待)状态,这种状态一直保持下去,此时称这个线程是死锁状态。
案例
package mainimport ("fmt""time")func main() {// 死锁和有缓存通道// demo43_1()// demo43_2()// demo43_3()demo43_4()}func demo43_4() {ch := make(chan int, 3)ch <- 1fmt.Println(<-ch)ch <- 2fmt.Println(<-ch)ch <- 3ch <- 4fmt.Println(len(ch))fmt.Println(cap(ch))}// deadlockfunc demo43_3() {ch := make(chan int, 3)ch <- 1ch <- 2ch <- 3ch <- 4 // 慈航出现死锁,超过缓存大小数量}// 不会产生死锁func demo43_2() {ch := make(chan int)// 子协程中阻塞 不影响主协程go func() {ch <- 1fmt.Println("执行goroutine")}()time.Sleep(time.Second)fmt.Println("demo43_2() end.")}// 死锁func demo43_1() {ch := make(chan int)ch <- 1}
selectselect
select和switch结构特别像,select中case的条件只能是I/O。
select执行过程
每个case必须是一个IO操作;哪个case可以执行就执行哪个;所有case都不能执行时,执行default;所有case都不能执行,且没有default,将会阻塞。
案例
package mainimport ("fmt""runtime""time")func main() {// select// demo42_1()// demo42_2()demo42_3()}// select和for循环结合使用func demo42_3() {ch := make(chan int)for i := 0; i <= 5; i++ {go func(arg int) {ch <- arg}(i)}for {select {case c := <-ch:fmt.Println("取出数据", c)default:time.Sleep(time.Second)// 没有default会出现死锁fmt.Println("default")}}fmt.Println("demo42_3() end")}// select和for循环结合使用func demo42_2() {ch := make(chan int)for i := 0; i <= 5; i++ {go func(arg int) {ch <- arg}(i)}for i := 0; i <= 5; i++ {select {case c := <-ch:fmt.Println("取出数据", c)default:// 没有default会出现死锁fmt.Println("default")}}fmt.Println("demo42_2() end")}func demo42_1() {runtime.GOMAXPROCS(1)ch1 := make(chan int, 1)ch2 := make(chan string, 1)ch1 <- 1ch2 <- "hello"select {case value := <-ch1:fmt.Println(value)case value := <-ch2:fmt.Println(value)default:fmt.Println("default")}}