go语言开发基础39 - 之go语言里的管道(channel)基本使用
- go语言里的管道(channel)类似unix中的管道(pipe)。
- channel里的数据遵循先进先出的原则。
- 不加锁多个goroutine同时操作管道里的数据时不会出现数据冲突。
- channel是有类型的。
channel定义与操作
1、int类型的管道定义、初始化与写和读数据
package main import "fmt" func main() { var intChan chan int // 定义int类型的管道,chan int为管道的类型 intChan = make(chan int, 4) // 初始化管道的长度 intChan <- 10 // 往管道里写入数据 num := <-intChan // 从管道里读取数据 fmt.Println(num) // 结果为:10 }
2、字符串类型的管道定义、初始化与写和读操作
package main import "fmt" func main() { var strChan chan string // 定义字符串类型的管道 strChan = make(chan string, 4) // 初始化管道长度 intChan <- "is string" // 往管道里写入数据 str := <-intChan // 从管道里读取数据 fmt.Println(str) // 结果为:is string }
3、map类型的管道定义、初始化与写和读操作
package main import "fmt" func main() { var mapChan chan map[string]string mapChan = make(chan map[string]string, 4) m := make(map[string]string) m["stu01"] = "001" m["stu02"] = "002" mapChan <- m // 将数据写入管道 outm := <-mapChan // 从管道获取数据 fmt.Println(outm) }
4、自定义结构体类型的管道定义、初始化与写和读操作
package main import "fmt" type Student struct { Name string } func main() { var stuChan chan *Student stuChan = make(chan *Student, 10) stu := Student{Name: "stu01"} stuChan <- &stu // 写入变量的地址 outstu := <-stuChan // 读取内容(读取的也是变量的地址) fmt.Println(*outstu) // 打印时需要加*号获取值,否则获取到的是地址 }
5、interface类型的管道定义、初始化、写、读操作与类型转换
package main import "fmt" type Student struct { Name string } func main() { var stuChan chan interface{} stuChan = make(chan interface{}, 10) stu := Student{Name: "interstu"} stuChan <- &stu var stu01 interface{} stu01 = <-stuChan fmt.Printf("stu01: type[%T],data[%s]\n", stu01, stu01) // 类型转换(将interface类型的变量stu01转换为Student类型的变量stu02) var stu02 *Student // 类型断言判断是否可以转换 stu02, ok := stu01.(*Student) if !ok { // 如果不可转换打印错误信息 fmt.Println("con not convert", ok) return } fmt.Printf("stu02: type[%T],data[%s]\n", stu02, stu02) // 打印转换结果 }
二、管道关闭后的写和读操作
2.1、管道关闭后再执行写操作时会报错示例
package main func main() { var wchan chan int // 定义管道并初始化管道长度 wchan = make(chan int, 5) wchan <-10 // 网管到里写入数据 close(wchan) // 关闭管道 wchan <-11 // 关闭管道后再执行写入数据操作时会报错:panic: send on closed channel }
2.2、管道关闭后在执行循环读取操作
示例一:使用for{}死循环读管道里的数据
package main import "fmt" func main() { var ch chan int // 定义并初始化管道 ch = make(chan int, 10) // 循环往管道里写入数据 for i := 0; i < 10; i++ { ch <- i } // 关闭管道 close(ch) // 使用死循环读取已关闭管道里的数据 --> 如果不做判断会陷入死循环 for { // 通过类型断言判断管道是否关闭(ok的值为true则表示管道没关闭了,如果值为false就表示管道关闭了) b, ok := <-ch if ok == false { // 如果不做这个判断ch关闭后再做死循环读取操作时会出现死循环 fmt.Println("chan is close") break } fmt.Println(b) } }
示例二:使用for range循环读取管道里的数据
package main import "fmt" func main() { // 定义管道并初始化 var ch chan int ch = make(chan int,100) // 往管到里写入数据 for i := 0; i < 100; i ++ { ch <- i } close(ch) // 往管道里写入数据后关闭管道(如果没有关闭管道,循环完了会陷入等待) for v := range ch { // for range循环完管道后循环自动结束(如果管道没关闭,循环完了会等待) fmt.Println(v) } }
三、通过类型断言判断管道关闭了没
package main import "fmt" func main() { // 定义与初始化管道 var ch chan int ch = make(chan int,100) // 管道里写入数据 for i := 0; i < 100; i ++ { ch <- i } close(ch) // 往管道里写入数据后关闭管道 for { var b int b, ok := <- ch // 判断一个管道是否关闭了(如果ok的值是false就表示管道关闭了) if ok == false { fmt.Println("channel is close") break } fmt.Println(b) } }
四、只读管道和只写管道
只读管道:var readChan <-chan string 函数参数:func recvdata(ch <-chan int){}
只写管道:var writeChan chan<- string 函数参数:func adddata(ch chan<- int) {}
package main import ( "fmt" ) // 往管道里写数据的函数 func adddata(ch chan<- int, exitChan chan struct{}) { // ch chan<- int:接收只写类型的管道 for i := 0; i < 10; i++ { ch <- i } close(ch) var a struct{} exitChan <- a } // 从管道里读取数据的函数 func recvdata(ch <-chan int, exitChan chan struct{}) { // ch <-chan int:接收只读类型参数 for { v, ok := <-ch if !ok { // fmt.Println("form chan get data failed") break } fmt.Println("recvdata",v) } var a struct{} exitChan <- a } func main() { // 定义数据管道和退出管道并初始化管道长度 var pipe chan int pipe = make(chan int, 10) exitChan := make(chan struct{}, 3) // 启动两个子进程执行写数据和读数据函数 go adddata(pipe, exitChan) go recvdata(pipe,exitChan) // 判断写数据和读数据的子进程是否结束 var total = 0; for _ = range exitChan { total ++ if total == 2 { break } } }
五、使用select读取管道里的数据
package main import ( "fmt" "time" ) func main() { // 定义两个管道并初始化 var ch chan int ch = make(chan int, 10) ch2 := make(chan int, 10) // 往两个管道里写数据 go func() { for i := 0; i < 30; i ++ { ch <- i ch2 <- i*i } }() // 使用死循环加select读数据 for { select { case v := <- ch: fmt.Println(v) case v := <- ch2: fmt.Println(v) default: fmt.Println("default info") time.Sleep(time.Second) } } }

浙公网安备 33010602011771号