MST

星途 面试题库

面试题:Go - 分析Goroutine卡住可能的资源竞争原因

在Go语言中,假设你遇到一个Goroutine卡住的情况,从资源竞争的角度分析,可能有哪些原因会导致这种情况发生?请举例说明如何排查这类资源竞争问题。
39.6万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

可能导致Goroutine卡住的资源竞争原因

  1. 共享变量竞争:多个Goroutine同时读写共享变量,没有合适的同步机制。例如:
package main

import (
    "fmt"
)

var num int

func increment() {
    for i := 0; i < 1000; i++ {
        num++
    }
}

func main() {
    for i := 0; i < 10; i++ {
        go increment()
    }
    // 这里如果没有合适的同步等待,可能num还没更新完就结束程序
    fmt.Println(num)
}

在此例中,多个Goroutine同时对num进行写操作,可能导致数据竞争,并且如果没有正确同步,可能会让Goroutine卡住。 2. 通道(Channel)死锁:当Goroutine在通道上发送或接收数据,但没有配对的操作时会发生死锁。例如:

package main

func main() {
    ch := make(chan int)
    ch <- 1 // 发送数据,但没有Goroutine接收,导致死锁
}
  1. 锁的滥用或死锁:例如多个Goroutine在获取不同顺序的锁时,可能会形成死锁。
package main

import (
    "sync"
)

var (
    mu1 sync.Mutex
    mu2 sync.Mutex
)

func goroutine1() {
    mu1.Lock()
    defer mu1.Unlock()
    mu2.Lock()
    defer mu2.Unlock()
    // 业务逻辑
}

func goroutine2() {
    mu2.Lock()
    defer mu2.Unlock()
    mu1.Lock()
    defer mu1.Unlock()
    // 业务逻辑
}

这里goroutine1goroutine2获取锁的顺序不一致,可能导致死锁,使得Goroutine卡住。

排查资源竞争问题的方法

  1. 使用-race标志:在编译和运行Go程序时,使用-race标志可以检测数据竞争。例如:
go run -race main.go

如果存在数据竞争,会输出详细的竞争信息,包括竞争发生的位置等。 2. 使用pprof分析:通过pprof可以分析程序的性能,包括是否存在阻塞等情况。首先在代码中引入net/http/pprof包,启动一个HTTP服务器来提供分析数据:

package main

import (
    "net/http"
    _ "net/http/pprof"
)

func main() {
    go http.ListenAndServe(":6060", nil)
    // 主程序逻辑
}

然后通过浏览器访问http://localhost:6060/debug/pprof/,可以查看各种分析数据,比如goroutineblock等,从中发现是否存在Goroutine卡住的情况及原因。 3. 添加日志:在关键代码位置添加日志,比如在获取锁、发送/接收通道数据前后打印日志,通过日志来分析程序执行流程,找出可能卡住的位置。例如:

package main

import (
    "fmt"
    "sync"
)

var mu sync.Mutex

func worker() {
    fmt.Println("Worker starting, trying to get lock")
    mu.Lock()
    defer mu.Unlock()
    fmt.Println("Worker got lock, doing work")
    // 业务逻辑
}