前言
在 Go 语言中,提倡通过通信来共享内存,而不是通过共享内存来通信,go中的Channel(一般简写为 chan) 管道提供了一种机制,它在两个并发执行的协程之间进行同步,并通过传递与该管道元素类型相符的值来进行通信,可以用来两个不同的协程之间共享数据
chan使用
chan类型
channel是一种类型,一种引用类型,声明类型时,可以使用
go
代码解读
复制代码
var chan2 = make(chan int)
或者
go
代码解读
复制代码
var chan2 = make(chan int64)
等等,创建chan用make实现,并且channel遵循先进先出原则
chan使用
chan在两个不同的协程之间通讯
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
go
代码解读
复制代码
package main import ( _ "fmt" "log" ) func main() { var chan2 = make(chan int) chan2 <- 1 data := <-chan2 log.Println("data的值为:", data) }
以上输出结果为
是因为无缓存线写之后,会阻塞
有缓存channel
go
代码解读
复制代码
package main import ( _ "fmt" "log" ) func main() { var chan2 = make(chan int, 1) chan2 <- 1 data := <-chan2 log.Println("data的值为:", data) }
以上输出结果为
但是超过定义的缓存,就会发生死锁
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取值。如
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遍历时,需要关闭管道,否则会报死锁
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只读没写,也会报死锁问题
go
代码解读
复制代码
package main import "fmt" func main() { ch := make(chan int64) data := <-ch fmt.Println(data) }
以上结果为
使用切片的channel就不会报死锁
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)
定义一个可读管道
css
代码解读
复制代码
func say(ch <-chan int) { for i := 0; i < 100; i++ { data := <-ch log.Println("结果为:", data) } }
定义一个只可写通道
css
代码解读
复制代码
func say1(ch chan<- int) { for i := 0; i < 100; i++ { ch <- i } }
使用如下
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机制可以设置阻塞超时
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,在并发中更好的进行写成之间的通讯