go常见小坑记录

1.修改切片必须用指针(修改可以,append地址会变,所以尽量用指针)

2.切片和map,先make再取地址传入函数(习惯)

3.切片保存的是值的拷贝,修改切片中某个对象的值,原始的值是不会改变


<think>我们正在讨论Go语言并发编程中的常见问题陷阱。根据引用内容,我们可以总结出一些关键点: 引用[1]提到:并发编程是一把双刃剑,可能陷入各种难以调试的并发Bug,尤其是Go内存模型中的陷阱。 引用[3]提到:竞态条件(Race Condition)是多个goroutine同时访问共享资源时导致的不确定行为。 引用[5]给出了一个并发处理任务的代码示例。 常见的并发问题陷阱包括: 1. 竞态条件(Race Conditions) 2. 死锁(Deadlocks) 3. 通道阻塞(Channel Blocking) 4. 内存可见性问题(Memory Visibility) 5. 并发安全的数据结构使用不当 我们将逐一解释这些问题,并给出代码示例说明。 注意:在编写代码示例时,我们将使用Go语言,并尽量保持简单以说明问题。 另外,根据用户要求,我们需要在回答中自然地添加引用标识。由于引用[1]、[3][5]与问题相关,我们会在适当的地方引用。 同时,我们需要在最后生成相关问题。</think>### Go并发编程常见问题与陷阱 Go语言的并发模型基于CSP(Communicating Sequential Processes)理论,通过goroutinechannel实现轻量级并发。以下是常见问题陷阱的分析: #### 1. 竞态条件(Race Condition) 当多个goroutine未同步地访问共享资源时,会导致不可预测的结果: ```go var counter int func increment() { counter++ // 非原子操作 } func main() { for i := 0; i < 1000; i++ { go increment() } time.Sleep(time.Second) fmt.Println(counter) // 结果可能小于1000 } ``` **陷阱**:`counter++`包含读取-修改-写入三步操作,多goroutine执行时可能互相覆盖[^3]。 **解决方案**: - 使用`sync.Mutex`互斥锁 - 使用`sync/atomic`原子操作 - 通过channel传递所有权 #### 2. 通道死锁(Channel Deadlock) ```go ch := make(chan int) ch <- 42 // 阻塞(无接收方) fmt.Println(<-ch) ``` **陷阱**: - 无缓冲通道需同步收发(发送方接收方需同时就绪) - 缓冲通道满时发送会阻塞 - 通道关闭后发送会panic **解决方案**: - 使用`select`+`default`实现非阻塞操作 - 明确关闭通道时机 - 使用`context`控制超时 #### 3. Goroutine泄漏 ```go func leak() { ch := make(chan int) go func() { <-ch // 永久阻塞 }() } ``` **陷阱**:未退出的goroutine会持续占用内存CPU,导致资源耗尽[^1]。 **解决方案**: - 使用`context.WithCancel()`控制生命周期 - 设置超时:`select { case <-time.After(1*time.Second): }` - 通过`sync.WaitGroup`等待结束 #### 4. 内存可见性问题 ```go var ready bool var data int func writer() { data = 42 ready = true // 编译器CPU可能重排指令 } func reader() { if ready { fmt.Println(data) // 可能看到0 } } ``` **陷阱**:编译器CPU的指令重排可能导致其他goroutine看到乱序执行结果[^1]。 **解决方案**: - 使用`sync/atomic`保证内存可见性 - 通过channel或`sync.Mutex`建立happens-before关系 #### 5. 工作池负载不均 ```go tasks := make(chan Task) for i := 0; i < 10; i++ { go worker(tasks) // 启动10个worker } // 提交任务时可能集中到少数worker ``` **陷阱**:Go的调度器可能将任务集中分配给部分worker,导致负载不均[^5]。 **解决方案**: - 使用带缓冲的channel:`tasks := make(chan Task, 100)` - 实现工作窃取(work stealing)算法 - 使用`runtime.GOMAXPROCS()`调整线程数 #### 6. 并发安全结构误用 ```go var m = make(map[string]int) func set(key string, val int) { m[key] = val // 并发写导致panic } ``` **陷阱**:Go的`map`、`slice`等原生结构非并发安全[^3]。 **解决方案**: - 使用`sync.Map`替代原生map - 通过`sync.RWMutex`实现读写锁 - 使用不可变数据结构 ### 最佳实践 1. **通信代替共享内存**:优先用channel传递数据而非共享变量[^2] 2. **控制并发量**:使用带缓冲channel或`semaphore.Weighted`限制goroutine数量 3. **防御性编程**:所有goroutine都应有`recover()`防止panic扩散 4. **检测工具**: - `go test -race` 检测竞态条件 - `go run -race` 运行时检测 - `pprof`分析goroutine堆栈
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值