https://www.jianshu.com/p/2a1146dc42c3 表达式求职
chan 泄漏的案例
函数中的全局变量,让子goroutine去引用,是线程安全的吗?
----------------------------
1.
无缓冲的通道,向通道中写入之后,如果没有被读取,则写入线程(协程)被阻塞,直到数据被读取
无缓冲的通道,如果通道没数据,则读取线程被阻塞,直到通道中有数据写入
2.channel底层原理
//https://www.jianshu.com/p/5046bf8593c3
3.channel 和 range
for range ch{
iValue := <- ch //会漏数据的,因为在for 循环中已经读取一个数据了
log.Debug("get value %d", iValue)
}
//正确用法 //注意每次返回iValue变量都是同一个(地址都一样),只是值变了
// channel被关闭的时候,会处理完channel中所有未读取的数据后,退出range,不会崩溃
for iValue := range ch{
log.Debug("get value %d, %d", iValue, &iValue)
}
4.channel作为参数传递
func TestChanAddr(chDevTask chan string) {
fmt.Printf("%p\n", chDevTask) //地址是一样的
time.Sleep(7 * time.Second)
strValue := "{xiaoyu:123}"
chDevTask <- strValue
fmt.Printf("write value success\n")
}
func main() {
log.LoadConfiguration("gotest-log.xml")
defer log.Close()
chDevTask := make(chan string)
fmt.Printf("%p\n", chDevTask) //注意,参数直接写chDevTask,而不是&chDevTask
go TestChanAddr(chDevTask)
for {
select {
case strValue := <-chDevTask:
{
fmt.Println(strValue)
}
}
}
return
4. channel + for + select
select 可以同时检查多个channel的读写状态
在select中,哪一个代码块被执行的算法大致如下:
检查每个case代码块
如果任意一个case代码块准备好发送或接收,执行对应内容
如果多余一个case代码块准备好发送或接收,随机选取一个并执行对应内容,所以检查多个channel的时候,都是和for循环结合使用
如果任何一个case代码块都没有准备好,等待
如果有default代码块,并且没有任何case代码块准备好,执行default代码块对应内容,过去个人觉得是打酱油的功能,不care,现在觉得很重要,否则本goroutine就阻塞了,我们很有可能还想做点其他事情
注意:
chTest := make(chan int)
for {
log.Info("begin select")
select {
case <-chTest:
//注意:chTest的值已经被读出来了
log.Info("doStuff done")
return
//default:
//log.Info("default")
}
log.Info("loop for")
}
没有default的时候,阻塞在 select,直到任何一个case变成真, log.info("loop for")不会被执行到,当case可执行后,执行完case,再次循环到select
有default的时候,如果case不可执行,for循环不会被阻塞,会执行default, log.info("loop for"),这里会执行死循环,会占满一核cpu 100%
for和select组合使用的时候,使用break无法跳出for循环
var chTest1 = make(chan int, 0)
func TestForSelect(){
forloop:
for{
select{
case chTest1 <- 1:
return
default:
fmt.Printf("default....\n")
//直接break,只会跳过后面的代码,但是不会退出for循环,可以使用标签forloop,或者goto,或者return
//return
//break
fmt.Printf("after break....\n")
break forloop
}
// fmt.Printf("default....\n")
// break
}
return
}
Go 语言中Select与for结合使用时可能会遇到的坑https://blog.csdn.net/u010525694/article/details/91320144
mainCh := make(chan bool)
ch := make(chan int) //有缓冲区的通道
timer := time.NewTimer(8 * time.Second)
go func(t *time.Timer){
for{
<- t.C
ch <- 6
t.Reset(10 * time.Second)
}
}(timer)
for{
select{
case <- mainCh: {
log.Debug("mainch get value")
}
case x := <- ch: {
log.Debug("ch get value %d", x)
}
// default:
// log.Debug("default")
}
}
for + select + 性能
2.5 GHz Intel Core i5, 16G,平均每秒可以执行860万次,不加select每秒大约900万次
iBeginTime := time.Now().UnixNano() / 1e6
iCount := 0
for {
iCount++
iSpan := time.Now().UnixNano()/1e6 - iBeginTime
if 1000 == iSpan {
log.Debug("count %d", iCount)
iBeginTime = time.Now().UnixNano() / 1e6
iCount = 0
}
}
select时间性能:
iCount := 0
iBeginTime := time.Now().UnixNano() / 1e6
for {
select {
case <-chDisConn:
{
}
default:
{
iCount++
iSpan := time.Now().UnixNano()/1e6 - iBeginTime
if 1000 == iSpan {
log.Debug("count %d", iCount)
iBeginTime = time.Now().UnixNano() / 1e6
iCount = 0
}
}
}
}
当select多个channel时间的时候,循环性能是随着下降的(有2个channel的时候,每秒400万次)
func TestSelect() {
chDisConn := make(chan int)
iCount := 0
iBeginTime := time.Now().UnixNano() / 1e6
chSign := make(chan int)
chSign1 := make(chan int)
chSign2 := make(chan int)
chSign3 := make(chan int)
chSign4 := make(chan int)
for {
select {
case <-chDisConn:
{
}
case <-chSign:
{
}
case <-chSign1:
{
}
case <-chSign2:
{
}
case <-chSign3:
{
}
case <-chSign4:
{
}
default:
{
iCount++
iSpan := time.Now().UnixNano()/1e6 - iBeginTime
if 1000 == iSpan {
log.Debug("count %d", iCount)
iBeginTime = time.Now().UnixNano() / 1e6
iCount = 0
}
}
}
}
}
5. chan和goroutine使用的时候,导致goroutine泄漏
https://www.cnblogs.com/xudong-bupt/p/10760423.html
package main
import (
"flag"
"log"
"net/http"
_ "net/http/pprof"
"time"
"fmt"
)
func demo (){
ch := make(chan int) //1
//ch := make(chan int, 1) //2
go func() { //写chan
time.Sleep(2 * time.Second)
ch <- 0 //注意,没有缓冲区的时候,这里一直阻塞,导致goroutine泄漏了
}()
select {
case <-ch: //读chan
fmt.Printf("exec success\n")
return
case <- time.After(1 *time.Second):
fmt.Printf("exec timeout\n")
return
}
}
func main() {
flag.Parse()
go func() {
log.Println(http.ListenAndServe("localhost:8080", nil))
}()
for i := 0; i < 400; i++ {
go demo()
}
fmt.Printf("sleep 1hour")
time.Sleep(60 * time.Minute)
}
结束goroutine
1. context的cancel
cancel()的时候,如果goroutine阻塞,则无法立即退出
WithDeadline,WithTimeout好像都是goroutine阻塞,则无法立即退出