面试题答案
一键面试使用方法值可能带来的潜在问题
- 资源竞争:当多个 goroutine 同时调用一个方法值时,如果方法内部涉及对共享资源(如变量)的读写操作,可能会引发资源竞争问题,导致数据不一致。
- 闭包相关问题:方法值可能会捕获其定义时的环境变量,如果在并发环境下这些环境变量的值发生变化,可能导致不可预期的行为。
Go 语言特性避免这些问题的方法
- 互斥锁(Mutex):通过
sync.Mutex
来保护共享资源,确保同一时间只有一个 goroutine 能够访问共享资源。
package main
import (
"fmt"
"sync"
)
type Counter struct {
value int
mu sync.Mutex
}
func (c *Counter) Increment() {
c.mu.Lock()
c.value++
c.mu.Unlock()
}
func (c *Counter) GetValue() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.value
}
func main() {
var wg sync.WaitGroup
counter := Counter{}
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
counter.Increment()
}()
}
wg.Wait()
fmt.Println("Final value:", counter.GetValue())
}
在上述代码中,Counter
结构体中的 mu
互斥锁保护了 value
变量,确保在并发调用 Increment
和 GetValue
方法时不会出现资源竞争。
- 通道(Channel):使用通道在 goroutine 之间进行通信,以避免共享状态。通道可以作为一种同步机制,确保数据的安全传递。
package main
import (
"fmt"
)
func worker(id int, jobs <-chan int, results chan<- int) {
for j := range jobs {
fmt.Printf("Worker %d started job %d\n", id, j)
result := j * 2
fmt.Printf("Worker %d finished job %d with result %d\n", id, j, result)
results <- result
}
}
func main() {
const numJobs = 5
jobs := make(chan int, numJobs)
results := make(chan int, numJobs)
for w := 1; w <= 3; w++ {
go worker(w, jobs, results)
}
for j := 1; j <= numJobs; j++ {
jobs <- j
}
close(jobs)
for a := 1; a <= numJobs; a++ {
<-results
}
close(results)
}
在这个例子中,jobs
通道用于向 worker goroutine 发送任务,results
通道用于接收任务结果。通过这种方式,避免了共享可变状态带来的问题。