面试题答案
一键面试- 死锁陷阱
- 无缓冲channel死锁:
- 产生原因:在无缓冲channel中,发送操作会阻塞直到有接收者准备好接收数据,接收操作会阻塞直到有数据发送过来。如果在一个goroutine中只进行发送操作,而在另一个goroutine中没有及时进行接收操作,就会导致死锁。例如:
- 无缓冲channel死锁:
package main
func main() {
ch := make(chan int)
ch <- 1 // 这里会阻塞,因为没有接收者
}
- **避免方法**:确保在发送数据前有对应的接收者准备好接收。可以启动一个goroutine来接收数据,例如:
package main
import "fmt"
func main() {
ch := make(chan int)
go func() {
fmt.Println(<-ch)
}()
ch <- 1
}
- **有缓冲channel死锁**:
- **产生原因**:当有缓冲channel已满,继续向其发送数据时会阻塞;当channel已空,继续从其接收数据时会阻塞。如果在这种阻塞情况下,没有其他goroutine来解除阻塞,就会导致死锁。例如:
package main
func main() {
ch := make(chan int, 1)
ch <- 1
ch <- 2 // 这里会阻塞,因为channel缓冲区已满
}
- **避免方法**:合理规划channel的缓冲区大小,并且确保在合适的时候进行接收操作以释放缓冲区空间。可以通过在发送一定数量数据后,启动goroutine进行接收,例如:
package main
import "fmt"
func main() {
ch := make(chan int, 1)
go func() {
for i := 0; i < 2; i++ {
fmt.Println(<-ch)
}
}()
ch <- 1
ch <- 2
}
- 未关闭channel导致泄漏
- 产生原因:如果一个goroutine从channel接收数据,但该channel永远不会关闭,这个goroutine将永远阻塞,造成资源泄漏。例如:
package main
func readChan(ch chan int) {
for {
v, ok := <-ch
if!ok {
return
}
// 处理数据v
}
}
如果调用readChan
时传入的ch
没有关闭,for
循环会一直阻塞。
- 避免方法:在数据发送完毕后,及时关闭channel。例如:
package main
import "fmt"
func readChan(ch chan int) {
for v := range ch {
fmt.Println(v)
}
}
func main() {
ch := make(chan int)
go func() {
for i := 0; i < 5; i++ {
ch <- i
}
close(ch)
}()
readChan(ch)
}
- 向已关闭channel发送数据
- 产生原因:向已关闭的channel发送数据会导致运行时恐慌(panic)。例如:
package main
func main() {
ch := make(chan int)
close(ch)
ch <- 1 // 这里会导致panic
}
- **避免方法**:在发送数据前,先判断channel是否关闭。可以通过使用`select`语句结合`default`分支来避免向已关闭的channel发送数据,例如:
package main
import "fmt"
func main() {
ch := make(chan int)
close(ch)
select {
case ch <- 1:
default:
fmt.Println("channel已关闭,不能发送数据")
}
}