面试题答案
一键面试1. 确保map并发读写安全及使用WaitGroup
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
dataMap := make(map[string]int)
mutex := sync.RWMutex{}
// 模拟写操作
for i := 0; i < 3; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
key := fmt.Sprintf("key%d", id)
mutex.Lock()
dataMap[key] = id
mutex.Unlock()
}(i)
}
// 模拟读操作
for i := 0; i < 2; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
key := fmt.Sprintf("key%d", id)
mutex.RLock()
value := dataMap[key]
mutex.RUnlock()
fmt.Printf("goroutine %d read value: %d\n", id, value)
}(i)
}
wg.Wait()
}
在上述代码中:
- 使用
sync.RWMutex
来确保map的并发读写安全。写操作时使用Lock
方法,读操作时使用RLock
方法。 sync.WaitGroup
用于等待所有的goroutine完成。在每个goroutine开始前调用wg.Add(1)
增加计数,在goroutine结束时调用wg.Done()
减少计数,最后通过wg.Wait()
阻塞主线程,直到所有计数归零,即所有操作完成。
2. 某个goroutine发生错误提前结束所有任务
package main
import (
"context"
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
dataMap := make(map[string]int)
mutex := sync.RWMutex{}
ctx, cancel := context.WithCancel(context.Background())
// 模拟写操作
for i := 0; i < 3; i++ {
wg.Add(1)
go func(id int, ctx context.Context) {
defer wg.Done()
key := fmt.Sprintf("key%d", id)
// 假设id为1时发生错误
if id == 1 {
cancel()
return
}
select {
case <-ctx.Done():
return
default:
mutex.Lock()
dataMap[key] = id
mutex.Unlock()
}
}(i, ctx)
}
// 模拟读操作
for i := 0; i < 2; i++ {
wg.Add(1)
go func(id int, ctx context.Context) {
defer wg.Done()
key := fmt.Sprintf("key%d", id)
select {
case <-ctx.Done():
return
default:
mutex.RLock()
value := dataMap[key]
mutex.RUnlock()
fmt.Printf("goroutine %d read value: %d\n", id, value)
}
}(i, ctx)
}
wg.Wait()
}
在修改后的代码中:
- 使用
context.Context
来处理某个goroutine发生错误提前结束所有任务的情况。 - 创建一个可取消的
context
,即ctx, cancel := context.WithCancel(context.Background())
。 - 在每个goroutine中,通过
select
语句监听ctx.Done()
通道。当某个goroutine发生错误时(这里假设id
为1时发生错误),调用cancel()
函数,所有监听ctx.Done()
通道的goroutine会收到信号并提前结束任务。