【Golang 面试题】每日 3 题(四十四)

✍个人博客:Pandaconda-优快云博客
📣专栏地址:http://t.csdnimg.cn/UWz06
📚专栏简介:在这个专栏中,我将会分享 Golang 面试中常见的面试题给大家~
❤️如果有收获的话,欢迎点赞👍收藏📁,您的支持就是我创作的最大动力💪

130. goroutine 阻塞、唤醒和退出状态

阻塞
channel 的读写操作、等待锁、等待网络数据、系统调用等都有可能发生阻塞,会调用底层函数 runtime.gopark(),会让出 CPU 时间片,让调度器安排其它等待的任务运行,并在下次某个时候从该位置恢复执行。

当调用该函数之后,goroutine 会被设置成 waiting 状态。

唤醒

处于 waiting 状态的 goroutine,在调用 runtime.goready() 函数之后会被唤醒,唤醒的 goroutine 会被重新放到 M 对应的上下文 P 对应的 runqueue 中,等待被调度。

当调用该函数之后,goroutine 会被设置成 runnable 状态。

退出

当 goroutine 执行完成后,会调用底层函数 runtime.Goexit()。

当调用该函数之后,goroutine 会被设置成 dead 状态。

131. Go goroutine 泄露原因

  • Goroutine 内进行 channel/mutex 等读写操作被一直阻塞。
  • Goroutine 内的业务逻辑进入死循环,资源一直无法释放。
  • Goroutine 内的业务逻辑进入长时间等待,有不断新增的 Goroutine 进入等待

132. Go goroutine 泄露场景

如果输出的 goroutines 数量是在不断增加的,就说明存在泄漏。

  1. nil channel
    channel 如果忘记初始化,那么无论你是读,还是写操作,都会造成阻塞。
func main() {
    fmt.Println("before goroutines: ", runtime.NumGoroutine())
    block1()
    time.Sleep(time.Second * 1)
    fmt.Println("after goroutines: ", runtime.NumGoroutine())
}
func block1() {
    var ch chan int
    for i := 0; i < 10; i++ {
        go func() {
            <-ch
        }()
    }
}

输出结果:

before goroutines:  1
after goroutines:  11
  1. 发送不接收

channel 发送数量超过 channel 接收数量,就会造成阻塞。

func block2() {
    ch := make(chan int)
    for i := 0; i < 10; i++ {
        go func() {
            ch <- 1
        }()
    }
}
  1. 接收不发送

channel 接收数量超过 channel 发送数量,也会造成阻塞。

func block3() {
    ch := make(chan int)
    for i := 0; i < 10; i++ {
        go func() {
            <-ch
        }()
    }
}
  1. http request body 未关闭
resp.Body.Close() 未被调用时,goroutine 不会退出。
func requestWithNoClose() {
    _, err := http.Get("https://www.baidu.com")
    if err != nil {
        fmt.Println("error occurred while fetching page, error: %s", err.Error())
    }
}
func requestWithClose() {
    resp, err := http.Get("https://www.baidu.com")
    if err != nil {
        fmt.Println("error occurred while fetching page, error: %s", err.Error())
        return
    }
    defer resp.Body.Close()
}
func block4() {
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func() {
                defer wg.Done()
                requestWithNoClose()
        }()
    }
}
var wg = sync.WaitGroup{}
func main() {
    block4()
    wg.Wait()
}

一般发起 http 请求时,需要确保关闭 body。

defer resp.Body.Close()
  1. 互斥锁忘记解锁

第一个协程获取 sync.Mutex 加锁了,但是他可能在处理业务逻辑,又或是忘记 Unlock 了。
因此导致后面的协程想加锁,却因锁未释放被阻塞了。

func block5() {
    var mutex sync.Mutex
    for i := 0; i < 10; i++ {
        go func() {
            mutex.Lock()
        }()
    }
}
  1. sync.WaitGroup 使用不当

由于 wg.Add 的数量与 wg.Done 数量并不匹配,因此在调用 wg.Wait 方法后一直阻塞等待。

func block6() {
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        go func() 
            wg.Add(2)
            wg.Done()
            wg.Wait()
        }()
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值