面试题答案
一键面试设计思路
- 错误处理与恢复:利用Go语言的defer和recover机制,在每个可能发生panic的goroutine中使用defer语句注册一个函数,该函数使用recover捕获panic,从而防止程序崩溃。
- 数据一致性:通过互斥锁(
sync.Mutex
)来保护共享资源的读写操作,确保同一时间只有一个goroutine能够访问共享资源,避免数据竞争和损坏。 - 性能优化:尽量减少锁的使用范围,只在实际读写共享资源时加锁,减少不必要的性能损耗。同时,对于频繁读取的场景,可以考虑使用读写锁(
sync.RWMutex
)提高并发性能。
数据结构
- 共享资源:定义一个结构体来表示共享资源,例如:
type SharedResource struct {
data int
mu sync.Mutex
}
- 错误处理与协调:可以使用一个通道(
chan
)来通知其他goroutine发生了panic并进行相应的处理。例如:
type PanicInfo struct {
err interface{}
from string
}
var panicChan = make(chan PanicInfo)
同步机制
- 互斥锁:使用
sync.Mutex
的Lock
和Unlock
方法来保护共享资源的读写操作。 - 读写锁:对于读多写少的场景,可以使用
sync.RWMutex
的RLock
、RUnlock
(读操作)和Lock
、Unlock
(写操作)方法。
核心代码实现
package main
import (
"fmt"
"sync"
)
type SharedResource struct {
data int
mu sync.Mutex
}
type PanicInfo struct {
err interface{}
from string
}
var panicChan = make(chan PanicInfo)
func (sr *SharedResource) Read() int {
sr.mu.Lock()
defer sr.mu.Unlock()
return sr.data
}
func (sr *SharedResource) Write(newData int) {
sr.mu.Lock()
defer sr.mu.Unlock()
sr.data = newData
}
func worker(sr *SharedResource, id int) {
defer func() {
if r := recover(); r != nil {
panicChan <- PanicInfo{
err: r,
from: fmt.Sprintf("worker %d", id),
}
}
}()
// 模拟一些可能导致panic的操作
for i := 0; i < 10; i++ {
sr.Write(i)
value := sr.Read()
fmt.Printf("Worker %d read value: %d\n", id, value)
if i == 5 {
// 模拟一个可能导致panic的操作
panic("Simulated panic")
}
}
}
func main() {
var wg sync.WaitGroup
sharedResource := &SharedResource{}
for i := 0; i < 3; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
worker(sharedResource, id)
}(i)
}
go func() {
for panicInfo := range panicChan {
fmt.Printf("Panic caught from %s: %v\n", panicInfo.from, panicInfo.err)
// 这里可以进行一些恢复操作,例如重置共享资源
}
}()
wg.Wait()
close(panicChan)
}
在上述代码中:
SharedResource
结构体包含共享数据data
和一个互斥锁mu
。Read
和Write
方法分别用于读取和写入共享资源,通过互斥锁保护数据一致性。worker
函数模拟一个goroutine,在其中进行共享资源的读写操作,并使用defer和recover捕获可能发生的panic,将panic信息发送到panicChan
。- 在
main
函数中,启动多个worker
goroutine,并通过wg
等待所有goroutine完成。同时,有一个goroutine从panicChan
中接收panic信息并进行处理。