面试题答案
一键面试检测资源竞争
在Go中,可以使用-race
标志来检测资源竞争。在编译和运行程序时带上该标志,Go运行时系统会记录所有的读写操作,并在检测到资源竞争时输出详细的报告。
例如:
go build -race
./your_binary
优化启动Goroutine以避免资源竞争
- 使用互斥锁(Mutex):
- 互斥锁可以保证在同一时间只有一个Goroutine能够访问共享资源。
- 示例代码如下:
package main
import (
"fmt"
"sync"
)
var (
counter int
mu sync.Mutex
)
func increment(wg *sync.WaitGroup) {
defer wg.Done()
mu.Lock()
counter++
mu.Unlock()
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go increment(&wg)
}
wg.Wait()
fmt.Println("Final counter value:", counter)
}
- 说明:
counter
是共享资源。mu
是一个sync.Mutex
类型的互斥锁。- 在
increment
函数中,通过mu.Lock()
获取锁,这样其他Goroutine在获取锁之前无法访问counter
。操作完成后,通过mu.Unlock()
释放锁,允许其他Goroutine获取锁并访问counter
。
- 使用读写锁(RWMutex):
- 当读操作远远多于写操作时,读写锁可以提高性能。多个Goroutine可以同时进行读操作,但写操作需要独占访问。
- 示例代码如下:
package main
import (
"fmt"
"sync"
)
var (
data int
rwMutex sync.RWMutex
)
func read(wg *sync.WaitGroup) {
defer wg.Done()
rwMutex.RLock()
fmt.Println("Read value:", data)
rwMutex.RUnlock()
}
func write(wg *sync.WaitGroup) {
defer wg.Done()
rwMutex.Lock()
data++
fmt.Println("Write value:", data)
rwMutex.Unlock()
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go read(&wg)
}
for i := 0; i < 2; i++ {
wg.Add(1)
go write(&wg)
}
wg.Wait()
}
- 说明:
data
是共享资源。rwMutex
是一个sync.RWMutex
类型的读写锁。read
函数使用rwMutex.RLock()
进行读锁定,允许多个Goroutine同时读。write
函数使用rwMutex.Lock()
进行写锁定,确保同一时间只有一个Goroutine进行写操作。
- 使用通道(Channel):
- 通过通道在Goroutine之间传递数据,而不是共享数据。
- 示例代码如下:
package main
import (
"fmt"
"sync"
)
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, result: %d\n", id, j, result)
results <- result
}
}
func main() {
const numJobs = 5
jobs := make(chan int, numJobs)
results := make(chan int, numJobs)
var wg sync.WaitGroup
for w := 1; w <= 3; w++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
worker(id, jobs, results)
}(w)
}
for j := 1; j <= numJobs; j++ {
jobs <- j
}
close(jobs)
go func() {
wg.Wait()
close(results)
}()
for r := range results {
fmt.Println("Result:", r)
}
}
- 说明:
jobs
通道用于向worker
Goroutine发送任务。results
通道用于从worker
Goroutine接收计算结果。- 通过通道传递数据,避免了多个Goroutine直接共享数据带来的资源竞争问题。