面试题答案
一键面试Goroutine 相较于传统线程的优势
- 轻量级:
- Goroutine非常轻量级,创建和销毁的开销极小。相比之下,传统线程创建和销毁的系统资源开销较大。在Go语言中,可以轻松创建数以万计的Goroutine,而创建同样数量的传统线程会消耗大量内存并可能导致系统资源耗尽。
- 调度效率高:
- Goroutine由Go运行时(runtime)的调度器管理,采用M:N调度模型(多个Goroutine映射到多个操作系统线程)。这种调度方式使得Goroutine的调度在用户态完成,避免了频繁的系统调用和上下文切换开销。而传统线程的调度由操作系统内核完成,上下文切换开销较大,尤其是在高并发场景下,会严重影响性能。
- 内存占用少:
- 每个Goroutine初始栈空间很小(通常为2KB左右),并且栈空间可以根据需要动态增长和收缩。传统线程的栈空间一般固定且较大(例如在Linux系统中,默认栈大小可能为8MB),这使得在高并发场景下,Goroutine的内存占用远远低于传统线程。
使用Goroutine处理并发的网络连接请求示例
package main
import (
"fmt"
"net"
)
func handleConnection(conn net.Conn) {
defer conn.Close()
buf := make([]byte, 1024)
n, err := conn.Read(buf)
if err != nil {
fmt.Println("Read error:", err)
return
}
message := string(buf[:n])
fmt.Println("Received:", message)
_, err = conn.Write([]byte("Message received successfully"))
if err != nil {
fmt.Println("Write error:", err)
return
}
}
func main() {
listener, err := net.Listen("tcp", ":8080")
if err != nil {
fmt.Println("Listen error:", err)
return
}
defer listener.Close()
fmt.Println("Server is listening on :8080")
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println("Accept error:", err)
continue
}
go handleConnection(conn)
}
}
在上述代码中:
main
函数通过net.Listen
监听TCP端口8080。- 每次调用
listener.Accept
接受一个新的网络连接时,会为该连接创建一个新的Goroutine来处理,即go handleConnection(conn)
。这样可以并行处理多个网络连接请求,而不会阻塞主线程,极大地提高了服务器的并发处理能力。 handleConnection
函数负责与客户端进行具体的数据读写操作,处理完一个连接后会关闭连接。