创建 channel
声明的管道后,需要使用make函数初始化之后才能使用。
创建 channel 的格式如下
go
ch := make(chan元素类型, 容量)
管道有发送(send)、接收(receive) 和关闭(close)三种操作。
发送和接收都使用<-符号。
管道是先进先出的。
管道是一个引用数据类型。
现在我们先使用以下语句定义一个管道:
管道的使用
package main
import (
"fmt"
)
func main() {
// 创建一个管道,管道的元素类型是int,容量是3
ch := make(chan int, 3)
// 向管道中发送数据,分别发送的数据是1,2,3
ch <- 1
ch <- 20
ch <- 30
a := <-ch
fmt.Println(a)
b := <-ch
fmt.Println(b)
c := <-ch
fmt.Println(c)
}
输出的值是:
1
20
30
说明:管道是先进先出的
管道的容量,长度,引用地址
package main
import (
"fmt"
)
func main() {
// 创建一个管道,管道的元素类型是int,容量是3
ch := make(chan int, 3)
// 向管道中发送数据,分别发送的数据是1,2,3
ch <- 1
ch <- 20
fmt.Printf("管道的引用地址是:%v\n", ch)
fmt.Printf("管道的长度是:%d\n", len(ch))
fmt.Printf("管道的容量是:%d\n", cap(ch))
}
管道的引用地址是:0x1f9b2ce28000
管道的长度是:2
管道的容量是:3
管道的长度是当前管道中添加的数据的数量。
管道的容量是你设置的管道容量。
管道的引用地址是管道的内存地址。
证明管道是引用数据类型
package main
import (
"fmt"
)
func main() {
// 创建一个管道,管道的元素类型是int,容量是3
ch := make(chan int, 3)
ch <- 1
ch <- 20
ch1 := ch
// 向管道ch1中发送数据,如果ch管道也发生改变了,说明管道是引用数据类型
ch1 <- 30
fmt.Printf("ch1管道的长度是:%d\n", len(ch1))
fmt.Printf("ch管道的长度是:%d\n", len(ch))
}
现在ch管道的长度是:3, ch1管道的长度也是:3
所以,可以证明管道是引用数据类型。
管道的阻塞(发送值)
package main
import (
"fmt"
)
func main() {
// 创建一个管道,管道的元素类型是int,容量是2
ch := make(chan int, 2)
ch <- 1
ch <- 20
// 这个时候由于管道的容量是2,所以不能发送第三个数据,会造成管道的阻塞
ch <- 30
fmt.Printf("ch管道的长度是:%d\n", len(ch))
}
fatal error: all goroutines are asleep - deadlock!
管道的阻塞(获取值)
package main
import (
"fmt"
)
func main() {
// 创建一个管道,管道的元素类型是int,容量是2
ch := make(chan int, 2)
ch <- 1
ch <- 20
value1 := <-ch
value2 := <-ch
// 这个时候由于管道的容量是2,所以不能获取第三个数据,会造成管道的阻塞
value3 := <-ch
fmt.Printf("value1是:%d\n", value1)
fmt.Printf("value2是:%d\n", value2)
fmt.Printf("value3是:%d\n", value3)
}
fatal error: all goroutines are asleep - deadlock!
for range遍历管道
使用for range遍历管道。
如果没有关闭管道,就使用 for range 遍历管道,
就会报个错误fatal error: all goroutines are asleep-deadlock!
通过for range来遍历管道数据管道,没有key,只有value。
关闭管道, for range才能遍历管道中的数据。
package main
import (
"fmt"
)
func main() {
var ch1 = make(chan int, 4)
ch1 <- 1
ch1 <- 20
ch1 <- 30
ch1 <- 40
// 关闭管道,才能遍历管道中的数据。否则会报错的
close(ch1)
// 循环遍历管道,注意:管道没有key,只有value
for value := range ch1 {
fmt.Printf("value是:%d\n", value)
}
}
value是:1
value是:20
value是:30
value是:40
for循环取管道中的数据,可以不关闭管道
package main
import (
"fmt"
)
func main() {
var ch1 = make(chan int, 4)
ch1 <- 1
ch1 <- 20
ch1 <- 30
ch1 <- 40
// for循环取管道中的数据,可以不关闭管道
for i := 0; i < 4; i++ {
fmt.Printf("管道中数据是:%d\n", <-ch1)
}
}
管道中数据是:1
管道中数据是:20
管道中数据是:30
管道中数据是:40
定义两个方法,一个方法给管道里面写数据,一个给管道里面读取数据。要求同步进行。
package main
import (
"fmt"
"sync"
"time"
)
var wg sync.WaitGroup
func writeData(ch chan int) {
// 往管道里面写数据,写10个数据
for i := 0; i < 10; i++ {
ch <- i * 100
fmt.Printf("写入管道中的数据是:%d,成功的\n", i*100)
time.Sleep(time.Millisecond * 50)
}
// 关闭管道
close(ch)
wg.Done()
}
func readData(ch chan int) {
for v := range ch {
fmt.Printf("读取管道中的数据是:%d,成功的\n", v)
time.Sleep(time.Millisecond * 50)
}
wg.Done()
}
func main() {
var ch1 = make(chan int, 10)
wg.Add(1)
go writeData(ch1)
wg.Add(1)
go readData(ch1)
wg.Wait()
fmt.Println("程序结束")
}
读取管道中的数据是:0,成功的
写入管道中的数据是:0,成功的
...
读取管道中的数据是:900,成功的
程序结束
为啥先显示的是读取呢?
因为:打印的时候也会耗费一定的时候,
它可以先读取到,说明是先有数据的哈,才能够读取到数据。
所以是没有问题的。
如果写入非常的慢,读取非常的快,上面的程序会有什么问题?
回答:没有问题的。我们可以看下
当读取的速度比写入的速度快,读取的时候,就会进行等待哈。
package main
import (
"fmt"
"sync"
"time"
)
var wg sync.WaitGroup
func writeData(ch chan int) {
// 往管道里面写数据,写10个数据
for i := 0; i < 10; i++ {
ch <- i * 100
fmt.Printf("写入管道中的数据是:%d,成功的\n", i*100)
// 写入数据的时候,等待100毫秒
time.Sleep(time.Millisecond * 100)
}
// 关闭管道
close(ch)
wg.Done()
}
func readData(ch chan int) {
for v := range ch {
fmt.Printf("读取管道中的数据是:%d,成功的\n", v)
// 读取数据的时候,等待50毫秒,现在读取的速度快,写入的速度慢
time.Sleep(time.Millisecond * 50)
}
wg.Done()
}
func main() {
var ch1 = make(chan int, 10)
wg.Add(1)
go writeData(ch1)
wg.Add(1)
go readData(ch1)
wg.Wait()
fmt.Println("程序结束")
}