MST

星途 面试题库

面试题:Go语言指针错误使用案例之并发场景下指针的隐患

在Go语言并发编程场景下,有如下代码: ```go package main import ( "fmt" "sync" ) var sharedPtr *int var mu sync.Mutex func worker(id int) { local := id * 2 mu.Lock() sharedPtr = &local mu.Unlock() fmt.Printf("Worker %d set sharedPtr to %d\n", id, *sharedPtr) } func main() { var wg sync.WaitGroup for i := 0; i < 5; i++ { wg.Add(1) go func(id int) { defer wg.Done() worker(id) }(i) } wg.Wait() fmt.Printf("Final value of sharedPtr: %d\n", *sharedPtr) } ``` 这段代码在并发执行时可能会出现什么问题?如何解决这些问题?请详细分析Go语言指针在并发场景下容易引发错误的原因及对应的解决方案,包括但不限于内存竞争、数据一致性等方面。
34.9万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

代码在并发执行时可能出现的问题

  1. 内存竞争:虽然使用了sync.MutexsharedPtr的赋值进行了保护,但fmt.Printf("Worker %d set sharedPtr to %d\n", id, *sharedPtr)这行代码存在内存竞争风险。因为在解锁mu后,sharedPtr可能在其他协程中被修改,导致这里打印的值不准确。
  2. 悬空指针localworker函数的局部变量,当worker函数返回后,local的内存可能被释放。而sharedPtr指向了这个局部变量,在其他协程中访问*sharedPtr时可能访问到已释放的内存,造成悬空指针问题。

Go语言指针在并发场景下容易引发错误的原因

  1. 内存管理:Go语言虽然有垃圾回收机制,但对于局部变量的内存释放时机并非完全确定。在并发场景下,当一个协程结束,其局部变量内存可能被迅速回收,而其他协程可能还在通过指针访问该内存。
  2. 共享资源访问:多个协程同时访问和修改共享指针,容易导致数据不一致和内存竞争问题。如果没有适当的同步机制,对共享指针的读写操作可能会交错进行,产生不可预测的结果。

解决方案

  1. 解决内存竞争问题:在打印*sharedPtr时也加锁,确保在打印过程中sharedPtr不会被其他协程修改。
package main

import (
    "fmt"
    "sync"
)

var sharedPtr *int
var mu sync.Mutex

func worker(id int) {
    local := id * 2
    mu.Lock()
    sharedPtr = &local
    mu.Unlock()
    mu.Lock()
    fmt.Printf("Worker %d set sharedPtr to %d\n", id, *sharedPtr)
    mu.Unlock()
}

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            worker(id)
        }(i)
    }
    wg.Wait()
    mu.Lock()
    fmt.Printf("Final value of sharedPtr: %d\n", *sharedPtr)
    mu.Unlock()
}
  1. 解决悬空指针问题:为sharedPtr分配独立的内存,而不是指向局部变量。
package main

import (
    "fmt"
    "sync"
)

var sharedPtr *int
var mu sync.Mutex

func worker(id int) {
    newVal := id * 2
    newPtr := &newVal
    mu.Lock()
    sharedPtr = newPtr
    mu.Unlock()
    mu.Lock()
    fmt.Printf("Worker %d set sharedPtr to %d\n", id, *sharedPtr)
    mu.Unlock()
}

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            worker(id)
        }(i)
    }
    wg.Wait()
    mu.Lock()
    fmt.Printf("Final value of sharedPtr: %d\n", *sharedPtr)
    mu.Unlock()
}

通过以上修改,既能保证数据一致性,避免内存竞争,又能防止悬空指针问题。