面试题答案
一键面试检测资源竞争
Go语言提供了内置的竞态检测器,通过在编译和运行时添加 -race
标志来启用。例如,假设有如下代码:
package main
import (
"fmt"
)
var counter int
func increment() {
counter++
}
func main() {
for i := 0; i < 10; i++ {
go increment()
}
fmt.Println("Final counter:", counter)
}
运行命令 go run -race main.go
,如果存在竞态条件,会输出类似如下信息:
==================
WARNING: DATA RACE
Write at 0x00c0000160a0 by goroutine 7:
main.increment()
/path/to/main.go:7 +0x2e
Previous read at 0x00c0000160a0 by main goroutine:
main.main()
/path/to/main.go:12 +0x6c
Goroutine 7 (running) created at:
main.main()
/path/to/main.go:10 +0x4e
==================
Final counter: 2
Found 1 data race(s)
exit status 66
解决资源竞争的技术手段
互斥锁(Mutex)
互斥锁用于保证在同一时刻只有一个协程可以访问共享资源。
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:", counter)
}
在这个例子中,mu.Lock()
确保只有一个协程可以进入临界区(counter++
),mu.Unlock()
释放锁。
读写锁(RWMutex)
当读操作远多于写操作时,读写锁是更好的选择。读操作可以并发执行,写操作会独占资源。
package main
import (
"fmt"
"sync"
)
var (
data int
rwmu sync.RWMutex
)
func read(wg *sync.WaitGroup) {
defer wg.Done()
rwmu.RLock()
fmt.Println("Read data:", data)
rwmu.RUnlock()
}
func write(wg *sync.WaitGroup) {
defer wg.Done()
rwmu.Lock()
data++
fmt.Println("Write data:", data)
rwmu.Unlock()
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
if i%2 == 0 {
wg.Add(1)
go write(&wg)
} else {
wg.Add(1)
go read(&wg)
}
}
wg.Wait()
}
读操作使用 rwmu.RLock()
和 rwmu.RUnlock()
,写操作使用 rwmu.Lock()
和 rwmu.Unlock()
。
通道(Channel)
通道可以用于在协程之间传递数据,从而避免直接共享资源。
package main
import (
"fmt"
"sync"
)
func producer(ch chan int, wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; i < 10; i++ {
ch <- i
}
close(ch)
}
func consumer(ch chan int, wg *sync.WaitGroup) {
defer wg.Done()
for val := range ch {
fmt.Println("Consumed:", val)
}
}
func main() {
var wg sync.WaitGroup
ch := make(chan int)
wg.Add(2)
go producer(ch, &wg)
go consumer(ch, &wg)
wg.Wait()
}
在这个例子中,生产者协程通过通道向消费者协程发送数据,而不是共享一个变量。这样可以避免资源竞争。