通道 channel + goroutine + select + 结束goroutine

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阻塞,则无法立即退出

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值