为啥要一起写呢?
因为chan经常配合着goroutine一起使用,用作于多个goroutine之间的通信
通道(chan)
一、声明通道
package main
import "fmt"
func main() {
// 声明一个缓冲为10的chan
chan1 := make(chan string, 10)
// chan1 len: 0 cap: 10
fmt.Printf("chan1 len: %d cap: %d", len(chan1), cap(chan1))
fmt.Println("")
// 声明一个没有缓冲的chan
chan2 := make(chan string)
// chan2 len: 0 cap: 0
fmt.Printf("chan2 len: %d cap: %d", len(chan2), cap(chan2))
// 声明一个只读通道
chan3 := make(<-chan string)
// 声明一个只写通道
chan4 := make(chan<- string)
}
不带缓冲的通道,进和出都会阻塞。
带缓冲的通道,进一次长度 +1,出一次长度 -1,如果长度等于缓冲长度时,再进就会阻塞。
二、通道的读 、写和关闭
package main
import "fmt"
func main() {
// 声明一个10缓冲区的chan
channel := make(chan int, 10)
// 往chan里 写入一个数据:1
channel <- 1
// 从chan里 读取一个数据
val := <-channel
fmt.Println(val) // 1
// 关闭chan
close(channel)
}
注意:
- chan的数据只能一个个依次读取,或者写入 。
- close后不能再写入,但是可以读,但是只读chan不能close。
携程(goroutine)
在Go语言中,协程(goroutine)是一种轻量级的线程,可以在单个Go程序中同时运行数千个协程。协程是通过go关键字创建的。使用协程的好处是可以实现异步执行,提高程序的并发性能。
一、携程的创建
package main
import "fmt"
func printNum() {
i := 1
for {
fmt.Println(i)
i++
}
}
func main() {
// 用 go关键字创建一个携程
go printNum()
// 匿名函数的方式
go func() {
i := 1
for {
fmt.Println(i)
i++
}
}()
}
二、携程间的通信
package main
import (
"fmt"
"sync"
)
// 该函数作用是不停的往 chan写数据
func senderData(channel chan<- int, group *sync.WaitGroup) {
defer group.Done()
for i := 1; i < 101; i++ {
channel <- i
fmt.Printf("senderData往channel写入:%d\n", i)
}
close(channel)
}
// 该函数作用是不停的从 chan 读取数据
func receiverData(channel <-chan int, group *sync.WaitGroup) {
defer group.Done()
for val := range channel {
fmt.Printf("receiverData从channel读取到:%d\n", val)
}
}
func main() {
channel := make(chan int, 2)
group := sync.WaitGroup{}
// 开启一个senderData协程
group.Add(1)
go senderData(channel, &group)
// 开启一个receiverData协程
group.Add(1)
go receiverData(channel, &group)
group.Wait()
}
这段代码很简单,首先写了两个函数:senderData和receiverData,前者是循环往channel里写1-100,写完就关闭通道,后者是不停的从通道里获取数据。
main函数里,声明了一个缓存为2的通道channel,然后用sync.WaitGroup定义一个协程组,然后每开启一个协程,group就+1,每个协程任务完成自己去调用**Done()**函数从组里删除,当组里协程都完成时,**group.Wait()**就退出阻塞了,整个main函数就结束了。
死锁
其实Go里还是很难出现死锁的,大家都知道Go是天然支持高并发的编程语言,但是保不准有“天才”程序员
(淦!说的就是我自己!上面那端业务代码,我第一次运行的时候就出现死锁了……)
就以上面的代码说明,senderData我们称为生产者,receiverData称为消费者,如果channel满了,但是生产者在放里面写数据,或者channel已经空了,但是消费者还在从里获取数据,就会导致协程阻塞,无法继续执行,从而导致死锁。因此在声明channel时给了一定量的缓冲,并且在生产者任务完成时close了channel,从而避免死锁出现。