并发编程通信方式之一-channel
-
概念-特性:go 语言channel 是引用类型,goroutine 间通过channel通信,遵循先入先出规则,保证收发数据顺序
-
创建方式:使用make 句柄创建,make(chan 数据类型)
-
向管道发送数据,格式如下:
test := make(chan interface{}) //此出不谈容量问题 test <- "hello" test <- 0 * 发送需要了解的理论知识点:向管道发送数据,如无接受方,发送操作持续阻塞,用户态协成完全由实现方控制,所以需要实现方要写对等代码,有发送方,一定需要有接收方,即使我们的管道是含容量,容量堆满,也终究阻塞,所以有无容量,理论并不矛盾 -
收发不对等报错:fatal error: all goroutines are asleep - deadlock!
-
通过管道接受数据,所涉及知识点如下:
- 非阻塞接收(使用该方式接收数据,语句不发生阻塞): - result, ok := <-ch result 接受到的数据,没有数据为0值,引用类型,没有值默认0值, ok 是否接受到数据,bool类型,ch 管道变量 - 阻塞接收(使用该方式接收数据,语句会发生阻塞): - result := <-ch - 丢弃接受数据,一般为了实现收发对等 - <-ch -
管道数据遍历,格式如下:
for data := range ch {} 通道不关闭,range 函数就不会结束,到没有接收到任何数据的时间,语句发生阻塞 -
实例代码(只需要关注管道收发业务即可,其它性能监控、数据竞争相关忽略):
package main import ( "fmt" "log" "net/http" _ "net/http/pprof" "os" "runtime" "sync" "sync/atomic" ) var wg sync.WaitGroup var timers int64 func main() { runtime.GOMAXPROCS(1) runtime.SetMutexProfileFraction(1) runtime.SetBlockProfileRate(1) go func() { if err := http.ListenAndServe(":6060", nil); err != nil { log.Fatal(err) } os.Exit(0) }() testChannel() select {} } func testChannel() { caps := 200000 ch := make(chan int,caps) wg.Add(caps) for i:= 0; i < caps; i++ { go func(a int) { defer wg.Done() atomic.AddInt64(&timers, 1) //原子安全读 if atomic.LoadInt64(&timers) == int64(caps) { log.Println("close ch========") defer close(ch) } log.Println("索引=",a) // 通过通道通知main的goroutine ch <- a fmt.Println("exit goroutine=",a) }(i) } // 接收数据 for data := range ch { fmt.Println("channel data = ",data) } wg.Wait() fmt.Println("all done") }