CSP -->不要以内存的方式来通信(要上锁),相反,要通过通信来共享内存channel 本身就是一个队列 先进先出channel 是引用类型 可以使用make初始化 make -- map、slice、channel
channel 写满的时候不能写,取空的时候不能取 很容易发生阻塞 很容易产生死锁
每个channel 只能放同一种类型
使用channel 收发操作都是在不同的goroutinuechannel无缓冲通道有缓冲通道
无缓冲通道
package mainimport ("fmt""time"
)func main() {//无缓冲通道 -->每次只能取一个ch1 := make(chan int)go senddata(ch1)time.Sleep(2 * time.Second)data := <-ch1fmt.Println("拿到通道数据:", data)data2 := <-ch1fmt.Println("拿到通道数据:", data2)//支持不同协程之间的通信// ch1 <- 3// data3 := <-ch1 //不行,会报错// fmt.Println("拿到通道数据:", data3)time.Sleep(2 * time.Second)
}func senddata(ch chan int) {ch <- 1fmt.Println("往通道里存放数据1")ch <- 2fmt.Println("往通道里存放数据2")
}
利用无缓冲通道 做协程的控制
package mainimport ("fmt""time"
)//利用无缓冲通道 做协程的控制
//主协程退出 子协程也会跟着退出。利用通道取控制主协程等待子协程执行完成再退出type signal struct{}func senddata(ch chan int) {fmt.Println("this is senddata...")time.Sleep(2 * time.Second)ch <- 1//子协程执行完了,就往通道里面放数据
}func main() {fmt.Println("this is main")ch1 := make(chan int)go senddata(ch1)fmt.Println("等待子协程执行完毕")<-ch1//从通道里拿到数据,才能继续执行,不然就处于阻塞状态fmt.Println("end...")
}
使用空结构体,更节省空间
package mainimport ("fmt""time"
)
//最好使用空结构体 模拟信号发生type signal struct{}func senddata(ch chan signal) {fmt.Println("this is senddata...")time.Sleep(2 * time.Second)ch <- signal{} //子协程执行完了,就往通道里面放数据
}func main() {fmt.Println("this is main")ch1 := make(chan signal)go senddata(ch1)fmt.Println("等待子协程执行完毕")<-ch1 //从通道里拿到数据,才能继续执行,不然就处于阻塞状态fmt.Println("end...")
}
通道的关闭——消费者不会因为没有数据产生死锁
package mainimport ("fmt""time"
)// 通道的关闭 生产者发送完毕数据,关闭通道。消费者就不会因为没有数据产生死锁
func senddata(ch chan string) {defer fmt.Println("数据发送完毕")for i := 0; i < 3; i++ {ch <- fmt.Sprintf("发送数据:%d", i)}//defer fmt.Println("数据发送完毕")defer close(ch) //关闭通道,拿取方判断通道是否关闭,避免产生死锁
}func main() {ch1 := make(chan string)go senddata(ch1)//方式一 判断通道是否关闭for {data := <-ch1//如果通道关闭,读取的就是数据类型的默认值if data == "" {break}fmt.Println("拿到的数据:", data)}//方式二for {data, ok := <-ch1//如果ok为false 表示通道关闭if !ok {break}fmt.Println("from channel:", data)}//方式三 range --> string slice map channel//range 会自动判断通道是否关闭for value := range ch1 {fmt.Println("from channel:", value)}
}
有缓冲通道
package mainimport ("fmt""time"
)// 有缓冲通道
func main() {ch1 := make(chan string, 6)go senddata(ch1)time.Sleep(2 * time.Second)for data := range ch1 {fmt.Println("读取通道数据:", data)}
}func senddata(ch chan string) {for i := 0; i < 10; i++ {ch <- fmt.Sprintf("data: %d", i)fmt.Println("往通道里放数据:", i)}defer close(ch)
}
监听通道
//select 监听通道
/*
select 同时监听多个通道操作,实现非阻塞通信和多路复用,实现高效的并发处理
select 语句监听多个通道操作,并且执行第一个准备好的操作,如果多个同时准备好,随机挑选一个执行如果没有通道准备好,select语句一直阻塞,直到至少有一个通道操作准备好
*/package mainimport ("fmt""time"
)func main() {ch1 := make(chan int)ch2 := make(chan int)ch3 := make(chan int)go func() {time.Sleep(10 * time.Second)ch1 <- 1}()go func() {time.Sleep(1 * time.Second)ch2 <- 2}()go func() {time.Sleep(3 * time.Second)ch3 <- 3}()for i := 0; i < 3; i++ {select {case msg := <-ch1:fmt.Println("接收到数据from ch1:", msg)case msg := <-ch2:fmt.Println("接收到数据from ch2:", msg)case msg := <-ch3:fmt.Println("接收到数据from ch3:", msg)case <-time.After(1 * time.Second):fmt.Println("设置超时时间,timeout!")// default:// fmt.Println("没有消息准备好!")}}
}