通道:用来发送类型化数据的管道(负责协程之间的通信)
FIFO通道。
声明:
var identifier chan datatype
为初始化的通道的值是nil
所有的类型都可以用于通道、空接口。
通道时引用类型。所以也使用make()函数类给它分配内存。
var ch1 chan string
ch1 = make(chan string)
ch1 := make (chan string)
通信操作符:
<-
流向通道
ch <- int1
流出
num := <- ch
<- ch
死锁:
1.runtime会检查main协程,如果main协程中使用的那个通道(无缓冲)没有另一端,则会报死锁
2.有缓冲:写入几个数据,但是因为没有另一段,通道满时无法继续写入,则死锁。
2.main协程等待一个协程给它发送数据,同时协程等待main协程给它发送数据,造成死锁。
2.不能避免除main协程之外的协程之间的死锁,需要自己检查。
通道阻塞:
没办法向通道中读取或写入数据的情况就是阻塞。
写入的情况: 无缓冲:接收者没准备好的情况
有缓冲:通道满的时候
读取的情况: 无缓冲:发送者没准备好的情况
有缓冲:通道为空
使用带缓冲的通道:
buf := 100
ch1 := make(chan string,buf)
使用有缓冲的通道:更具有伸缩性。
信号量模式:
协程向另一个协程写入数据,表示执行结束(另一个协程等待此协程的数据)
func A(A_B,B_A chan int){
for {
<- B_A
fmt.Println("A")
A_B<-1
}
}
func B(A_B,B_A chan int){
for {
<-A_B
fmt.Println("B")
B_A<-1
}
}
匿名函数(协程)内使用通道
go func() {
chA_B<-1
}()
带缓冲通道实现资源型信号量
1.通道容量和资源数总数相同
2.通道内元素长度与当前被使用的资源数相同
3.通道容量减去通道元素长度等于未使用的元素总数
P、V操作
P操作向通道内写入多个元素以表示有多少资源被占用
V操作向通道内写入多个元素以表示有多少元素被释放
lock锁(建议为lock锁设置一个单独的通道(缓冲为1))
lock,向通道内写满元素,通道被占满,其它协程使用lock就会阻塞。
unlock,释放通道里所有的元素,使得其它协程可以继续它们的lock操作。
通道元素的读写是原子操作。
func P(n int){
e := new(empty.Empty)
for i:= 0;i<n ;i++ {
ch1<-*e
}
}
func V(n int){
for i:= 0 ;i <n ;i++ {
<-ch1
}
}
func wait(n int){
P(n)
}
func Siganl(n int){
V(n)
}
chLock := make(chan int,1)
func P(){ chLock<-1}
func V(){ <- chLcok}
func lock(){
P(1)
}
func unlock(){
V(1)
}
通道使用for循环
for v := range ch{
...............
}
普通for(迭代)
for i:= 0 ; i <c.len() ; i++{
ch <- c.items[i]
}
通道的方向:(常用于协程的形参,使得通道可以根据固定的方向来传输)
因为会出现在协程的函数里,一个通道既可以写也可以读,但是在大部分情况下(锁就是个特例)
我们的通道是两个协程之间的定向传输通道。
var send_only chan<- int (只发送)
var recv_only <- chan int (只接收,只接收的通道无法关闭,因为关闭功能对它来说没用)
通道被关闭:(还可以接收值)
close(ch) 实际上是将通道标记为无法通过<- 接收值。
(向已经关闭的通道写入数据和第二次关闭通道都会导致panic)
测试通道是否被关闭
v , ok := <- ch (读的第二个返回值是是否正常读取,读取原本就有的元素返回true,但是在通道关闭后,就是读取完所有元素也可以继续读取,之后也不会发生panic,会读取到一个零值和false)
(出现false可以表示为通道被关闭了。)
实现非阻塞通道(都不能关闭)的读取,使用select
select {
case u:= <- ch1:
...
case v:= <- ch2:
...
...
default: // no value ready to be received
...
}
1> 如果都阻塞了(没有default),会等待
2> 如果多个可以处理,随机选一个
3> 都不能处理,执行default
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
ch3 := make(chan int)
go func() {
for {
time.Sleep(1000)
ch1<- 1
ch2<- 2
ch3<- 3
}
}()
for {
time.Sleep(100)
select {
case num := <-ch1:
fmt.Println(num)
case num := <- ch2:
fmt.Println(num)
case num := <- ch3:
fmt.Println(num)
default:
fmt.Println("no nimber")
}
}
通道、超时、计时器
time包:
time.Ticker结构体,以指定的时间间隔重复向通道C(内部字段)发送时间值。
协程和恢复(recover)
协程内发生panicking和其它协程无关。所有的处理也仅仅是在这个协程上
使用锁(sunc.lock)的情景:
1>访问共享数据结构中的缓存信息
2>保存引用程序上下文和状态信息数据
使用通道的情景:
1>与异步操作的结果进行交互
2>分发任务
3>传递数据所有权