前言
在 Go 语言中,提倡通过通信来共享内存,而不是通过共享内存来通信,go中的Channel(一般简写为 chan) 管道提供了一种机制,它在两个并发执行的协程之间进行同步,并通过传递与该管道元素类型相符的值来进行通信,可以用来两个不同的协程之间共享数据
chan使用
chan类型
channel是一种类型,一种引用类型,声明类型时,可以使用
代码语言:javascript复制go 代码解读复制代码var chan2 = make(chan int)
或者
代码语言:javascript复制go 代码解读复制代码var chan2 = make(chan int64)
等等,创建chan用make实现,并且channel遵循先进先出原则
chan使用
chan在两个不同的协程之间通讯
代码语言:javascript复制go 代码解读复制代码package main
import (
"fmt"
_ "fmt"
"time"
)
func main() {
var chan2 = make(chan int)
go say(chan2)
go say1(chan2)
time.Sleep(5 * time.Second)
}
func say(a chan<- int) {
for i := 0; i < 100; i {
a <- i
}
}
func say1(a <-chan int) {
for i := 0; i < 100; i {
data := <-a
fmt.Println(data)
}
}
chan使用注意
chan分为无缓存 channel 和有缓存 channel,例如
有缓存 channel
代码语言:javascript复制go 代码解读复制代码package main
import (
_ "fmt"
"log"
)
func main() {
var chan2 = make(chan int)
chan2 <- 1
data := <-chan2
log.Println("data的值为:", data)
}
以上输出结果为
是因为无缓存线写之后,会阻塞
有缓存channel
代码语言:javascript复制go 代码解读复制代码package main
import (
_ "fmt"
"log"
)
func main() {
var chan2 = make(chan int, 1)
chan2 <- 1
data := <-chan2
log.Println("data的值为:", data)
}
以上输出结果为
但是超过定义的缓存,就会发生死锁
代码语言:javascript复制go 代码解读复制代码package main
import (
_ "fmt"
"log"
)
func main() {
var chan2 = make(chan int, 1)
chan2 <- 1
chan2 <- 2
data := <-chan2
log.Println("data的值为:", data)
}
以上输出结果为
从chan取值
使用range可以从channel取值。如
代码语言:javascript复制go 代码解读复制代码package main
import "log"
func main() {
ch := make(chan int64)
go say(ch)
for i := range ch {
data := i
log.Println(data)
}
}
func say(ch chan int64) {
for i := 0; i < 100; i {
ch <- int64(i)
}
close(ch)
}
以上结果为
但是要注意的是,在使用range遍历时,需要关闭管道,否则会报死锁
代码语言:javascript复制go 代码解读复制代码package main
import "log"
func main() {
ch := make(chan int64)
go say(ch)
for i := range ch {
data := i
log.Println(data)
}
}
func say(ch chan int64) {
for i := 0; i < 100; i {
ch <- int64(i)
}
//close(ch)
}
以上输出结果为
channel只读没写,也会报死锁问题
代码语言:javascript复制go 代码解读复制代码package main
import "fmt"
func main() {
ch := make(chan int64)
data := <-ch
fmt.Println(data)
}
以上结果为
使用切片的channel就不会报死锁
代码语言:javascript复制go 代码解读复制代码package main
import (
"fmt"
"time"
)
func main() {
channels := make([]chan int, 2)
for i := 0; i < 2; i {
go func(ch chan int) {
time.Sleep(time.Second)
ch <- 1
}(channels[i])
}
for ch := range channels {
fmt.Println("执行结果为:", ch)
}
fmt.Println("执行结束=====================")
}
chnnel可读可写
channel 可以分为 3 种类型:
- 只读 channel,单向 channel
- 只写 channel,单向 channel
- 可读可写 channel 默认情况下,都是可读可写的,如
go 代码解读复制代码ch := make(chan int64)
定义一个可读管道
代码语言:javascript复制css 代码解读复制代码func say(ch <-chan int) {
for i := 0; i < 100; i {
data := <-ch
log.Println("结果为:", data)
}
}
定义一个只可写通道
代码语言:javascript复制css 代码解读复制代码func say1(ch chan<- int) {
for i := 0; i < 100; i {
ch <- i
}
}
使用如下
代码语言:javascript复制go 代码解读复制代码package main
import (
"log"
"time"
)
func main() {
var ch = make(chan int)
go say(ch)
go say1(ch)
time.Sleep(5 * time.Second)
}
func say(ch <-chan int) {
for i := 0; i < 100; i {
data := <-ch
log.Println("结果为:", data)
}
}
func say1(ch chan<- int) {
for i := 0; i < 100; i {
ch <- i
}
}
chan超时
chan配合select机制可以设置阻塞超时
代码语言:javascript复制go 代码解读复制代码package main
import (
"fmt"
"time"
)
func main() {
var ch = make(chan int)
go say(ch)
select {
case data, ok := <-ch:
fmt.Println(data, ok)
case <-time.After(3 * time.Second):
fmt.Println("================超时")
}
}
func say(ch chan int) {
time.Sleep(5 * time.Second)
ch <- 1
fmt.Println("==============执行")
}
总结
合理的使用channel,在并发中更好的进行写成之间的通讯