外观
manus-go
约 9555 字大约 32 分钟
2025-06-11
目录
模块一:Go语言基础与数据类型
1. 什么是Go语言?Go语言有哪些特点?
答案: Go语言是Google开发的一种静态强类型、编译型语言。主要特点包括:
- 简洁的语法
- 高效的编译速度
- 内置并发支持
- 垃圾回收机制
- 跨平台支持
- 丰富的标准库
2. Go语言和Java有什么区别?
答案:
- Go不允许函数重载,必须具有方法和函数的唯一名称,而Java允许函数重载
- 在速度方面,Go的速度要比Java快
- Java默认允许多态,而Go没有传统意义上的多态
- Go语言使用HTTP协议进行路由配置,而Java使用不同的路由机制
- Go代码可以自动扩展到多个核心,而Java并不总是具有这些的可扩展性
- Go语言的继承通过匿名组合完成,支持多继承;而Java的继承通过extends关键字完成,不支持多继承
3. Go是面向对象的语言吗?
答案: 是的,也不是。原因是:
- Go有类型和方法,并且允许面向对象的编程风格,但没有类型层次
- Go中的"接口"概念提供了一种不同的方法
- Go中的方法比C++或Java中的方法更通用:它们可以为任何类型的数据定义
- Go由于缺乏类型层次,Go中的"对象"比C++或Java等语言更轻巧
4. 什么是面向对象?
答案: 面向对象编程(OOP)是一种基于"对象"概念的编程范式,它可以包含数据和代码:
- 数据以字段的形式存在(通常称为属性性或成员变量)
- 代码以方法的形式存在(通常称为方法)
- 对象自己的程序可以访问并经常修改自己的数据字段
- 对象经常被定义为类的一个实例
- 对象利用属性和方法的私有/受保护/公共可见性,对象的内部状态受到保护
5. nil切片和空切片有什么区别?
答案:
// nil切片
var s1 []int
fmt.Println(s1 == nil) // true
fmt.Println(len(s1)) // 0
fmt.Println(cap(s1)) // 0
// 空切片
s2 := make([]int, 0)
fmt.Println(s2 == nil) // false
fmt.Println(len(s2)) // 0
fmt.Println(cap(s2)) // 0
s3 := []int{}
fmt.Println(s3 == nil) // false
6. 字符串转成byte数组,会发生内存拷贝吗?
答案: 会发生内存拷贝。字符串在Go中是不可变的,当转换为[]byte时,需要分配新的内存空间并复制数据。
7. 翻转含有中文、数字、英文字母的字符串
答案:
func reverseString(s string) string {
runes := []rune(s)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes)
}
8. 拷贝大切片一定比小切片代价大吗?
答案: 不一定。切片的拷贝代价取决于切片中元素的类型和数量,而不是切片本身的大小。切片本身只是一个包含指针、长度和容量的结构体。
9. map不初始化使用会怎么样?
答案:
var m map[string]int
m["key"] = 1 // panic: assignment to entry in nil map
对nil map进行写操作会panic,但读操作是安全的,会返回零值。
10. map不初始化长度和初始化长度的区别
答案:
// 不初始化长度
m1 := make(map[string]int)
// 初始化长度
m2 := make(map[string]int, 100)
初始化长度可以减少map扩容的次数,提高性能,但不会限制map的最大容量。
11. map承载多大,大了怎么办?
答案: map的容量理论上只受内存限制。当map变得很大时,可以考虑:
- 分片存储
- 使用更高效的数据结构
- 数据压缩
- 分布式存储
12. map的iterator是否安全?能不能一边delete一边遍历?
答案: map的遍历顺序是随机的,不是线程安全的。可以一边delete一边遍历,但删除的元素可能在后续遍历中出现或不出现。
13. 字符串不能改,那转成数组能改吗,怎么改?
答案:
s := "hello"
bytes := []byte(s)
bytes[0] = 'H'
result := string(bytes)
fmt.Println(result) // "Hello"
14. 怎么判断一个数组是否已经排序?
答案:
func isSorted(arr []int) bool {
for i := 1; i < len(arr); i++ {
if arr[i] < arr[i-1] {
return false
}
}
return true
}
15. 普通map如何不用锁解决协程安全问题?
答案:
- 使用sync.Map
- 使用channel进行串行化访问
- 每个goroutine使用独立的map,最后合并结果
16. array和slice的区别
答案:
- array是值类型,slice是引用类型
- array的长度是固定的,slice的长度是可变的
- array作为函数参数时会发生值拷贝,slice传递的是引用
17. json包变量不加tag会怎么样?
答案: 不加tag时,json包会使用字段名作为json的key,且只有首字母大写的字段才会被序列化。
18. reflect(反射包)如何获取字段tag?为什么json包不能导出私有变量的tag?
答案:
type User struct {
Name string `json:"name"`
age int `json:"age"`
}
func getTag() {
t := reflect.TypeOf(User{})
field, _ := t.FieldByName("Name")
fmt.Println(field.Tag.Get("json")) // "name"
}
json包不能导出私有变量是因为反射无法访问其他包的私有字段。
19. 零切片、空切片、nil切片是什么?
答案:
// nil切片
var s1 []int
// 空切片
s2 := make([]int, 0)
s3 := []int{}
// 零切片(长度为0但容量不为0)
s4 := make([]int, 0, 10)
20. slice深拷贝和浅拷贝
答案:
// 浅拷贝
s1 := []int{1, 2, 3}
s2 := s1
// 深拷贝
s3 := make([]int, len(s1))
copy(s3, s1)
21. map触发扩容的时机,满足什么条件时扩容?
答案: 当负载因子超过6.5时,map会触发扩容。负载因子 = 元素个数 / 桶个数。
22. map扩容策略是什么?
答案:
- 当负载因子过大时,进行增量扩容,桶数量翻倍
- 当overflow bucket过多时,进行等量扩容,重新排列数据
23. 自定义类型切片转字节切片和字节切片转回自定义类型切片
答案:
type MyInt int
func convertSlice() {
// 自定义类型切片转字节切片
ints := []MyInt{1, 2, 3}
bytes := *(*[]byte)(unsafe.Pointer(&ints))
// 字节切片转回自定义类型切片
result := *(*[]MyInt)(unsafe.Pointer(&bytes))
}
24. make和new什么区别?
答案:
- new返回指针,make返回值
- new只分配内存并零值初始化,make会进行初始化
- new可用于任何类型,make只能用于slice、map、channel
25. slice,map,channel创建的时候的几个参数什么含义?
答案:
// slice
make([]int, len, cap) // 类型,长度,容量
// map
make(map[string]int, size) // 类型,初始容量
// channel
make(chan int, buffer) // 类型,缓冲区大小
26. slice,len,cap,共享,扩容
答案:
- len:当前切片的长度
- cap:当前切片的容量
- 多个切片可以共享底层数组
- 当len超过cap时会触发扩容,容量翻倍(小于1024时)或增长25%(大于1024时)
27. 线程安全的map怎么实现?
答案:
- 使用sync.Map
- 使用读写锁保护普通map
- 使用channel串行化访问
28. go slice 和 array 区别
答案:
- slice是动态数组,array是静态数组
- slice是引用类型,array是值类型
- slice有len和cap概念,array只有len
29. go struct能不能比较?
答案: struct可以比较,但有条件:
- 所有字段都是可比较的类型
- 不包含slice、map、function类型的字段
30. map如何顺序读取?
答案:
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
fmt.Println(k, m[k])
}
31. go中怎么实现set?
答案:
type Set map[string]struct{}
func (s Set) Add(item string) {
s[item] = struct{}{}
}
func (s Set) Contains(item string) bool {
_, exists := s[item]
return exists
}
32. 使用值为nil的slice、map会发生什么?
答案:
- nil slice:读取安全,追加安全,但索引访问会panic
- nil map:读取安全返回零值,写入会panic
33. Golang有没有this指针?
答案: Go没有this指针,但方法接收者类似于this的作用。
34. Golang语言中局部变量和全局变量的缺省值是什么?
答案: 所有变量都会被初始化为其类型的零值:
- 数值类型:0
- 字符串:""
- 布尔:false
- 指针、slice、map、channel、interface:nil
35. Golang中的引用类型包含哪些?
答案: slice、map、channel、interface、function、pointer
模块二:并发编程与Goroutine
36. 什么是Goroutine?它与线程有什么区别?
答案: Goroutine是Go语言中的轻量级线程,由Go运行时管理。区别:
- Goroutine的栈大小是动态的,初始只有2KB
- 线程的栈大小通常是固定的,一般为2MB
- Goroutine的创建和销毁成本更低
- Goroutine由Go调度器调度,线程由操作系统调度
37. 如何创建一个Goroutine?
答案:
// 使用go关键字
go func() {
fmt.Println("Hello from goroutine")
}()
// 调用函数
go myFunction()
38. 什么是Channel?有哪些类型?
答案: Channel是Goroutine之间通信的管道。类型包括:
- 无缓冲channel:
make(chan int)
- 有缓冲channel:
make(chan int, 10)
- 只读channel:
<-chan int
- 只写channel:
chan<- int
39. 有缓冲channel和无缓冲channel的区别?
答案:
- 无缓冲channel:同步的,发送和接收必须同时准备好
- 有缓冲channel:异步的,只要缓冲区未满就可以发送,只要缓冲区非空就可以接收
40. 对已经关闭的channel进行读写,会怎么样?为什么?
答案:
ch := make(chan int, 1)
close(ch)
// 读取已关闭的channel
v, ok := <-ch // v=0, ok=false,不会panic
// 写入已关闭的channel
ch <- 1 // panic: send on closed channel
41. 对未初始化的channel进行读写,会怎么样?为什么?
答案:
var ch chan int
// 读取nil channel
<-ch // 永久阻塞
// 写入nil channel
ch <- 1 // 永久阻塞
42. go中channel在项目中使用的场景举例
答案:
- 消息传递、消息过滤
- 信号广播
- 事件订阅与广播
- 请求、响应转发
- 任务分发
- 限流控制
- 超时控制
43. go中使用chan要注意什么?
答案:
- 使用for-range读取channel,安全且便利
- 读已关闭的channel会返回零值,需要使用ok检测
- select可以同时监控多个通道
- 防止goroutine泄露
- 有缓冲通道是异步的,无缓冲通道是同步的
- 使用select和time.After实现超时控制
44. 说一下go中的CSP模型
答案: CSP(Communicating Sequential Processes)并发模型:
- Go的CSP并发模型通过goroutine和channel实现
- 不同于传统的多线程通过共享内存来通信
- CSP讲究"以通信的方式来共享内存"
- 核心思想:Don't communicate by sharing memory; share memory by communicating
45. 说一下go中channel是不是线程安全的?
答案: Channel是线程安全的:
- 发送和接收操作都是原子性的
- 内部使用mutex保护
- 设计目标就是在多任务间安全传递数据
46. 什么是select语句?如何使用?
答案:
select {
case v := <-ch1:
fmt.Println("Received from ch1:", v)
case ch2 <- 42:
fmt.Println("Sent to ch2")
case <-time.After(1 * time.Second):
fmt.Println("Timeout")
default:
fmt.Println("No channel ready")
}
47. 如何实现超时控制?
答案:
func withTimeout(ch <-chan int, timeout time.Duration) (int, error) {
select {
case v := <-ch:
return v, nil
case <-time.After(timeout):
return 0, errors.New("timeout")
}
}
48. 什么是sync.WaitGroup?如何使用?
答案:
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
fmt.Println(i)
}(i)
}
wg.Wait()
49. 什么是sync.Mutex?如何使用?
答案:
var mu sync.Mutex
var count int
func increment() {
mu.Lock()
defer mu.Unlock()
count++
}
50. sync.Mutex和sync.RWMutex的区别?
答案:
- Mutex:互斥锁,同时只能有一个goroutine访问
- RWMutex:读写锁,允许多个读者同时访问,但写者独占
51. 什么是sync.Once?使用场景?
答案:
var once sync.Once
var instance *Singleton
func GetInstance() *Singleton {
once.Do(func() {
instance = &Singleton{}
})
return instance
}
52. 什么是sync.Pool?使用场景?
答案: sync.Pool是对象池,用于复用对象减少GC压力:
var pool = sync.Pool{
New: func() interface{} {
return make([]byte, 1024)
},
}
func usePool() {
buf := pool.Get().([]byte)
defer pool.Put(buf)
// 使用buf
}
53. 如何控制goroutine的数量?
答案:
// 使用带缓冲的channel
semaphore := make(chan struct{}, 10)
for i := 0; i < 100; i++ {
semaphore <- struct{}{}
go func() {
defer func() { <-semaphore }()
// 执行任务
}()
}
54. 什么是goroutine泄露?如何避免?
答案: Goroutine泄露是指goroutine无法正常退出,一直占用资源。避免方法:
- 使用context控制goroutine生命周期
- 确保channel能够正常关闭
- 避免无限循环的goroutine
55. 如何优雅地关闭goroutine?
答案:
func worker(ctx context.Context) {
for {
select {
case <-ctx.Done():
return
default:
// 执行工作
}
}
}
ctx, cancel := context.WithCancel(context.Background())
go worker(ctx)
// 关闭时调用cancel()
56. 什么是context包?如何使用?
答案: context包用于在goroutine之间传递取消信号、超时信息和请求范围的值:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
select {
case <-ctx.Done():
fmt.Println("Timeout or cancelled")
}
57. context.WithCancel、WithTimeout、WithDeadline的区别?
答案:
- WithCancel:手动取消
- WithTimeout:超时自动取消
- WithDeadline:到达指定时间自动取消
58. 如何实现一个goroutine池?
答案:
type Pool struct {
work chan func()
sem chan struct{}
}
func New(size int) *Pool {
return &Pool{
work: make(chan func()),
sem: make(chan struct{}, size),
}
}
func (p *Pool) Schedule(task func()) {
select {
case p.work <- task:
case p.sem <- struct{}{}:
go p.worker(task)
}
}
func (p *Pool) worker(task func()) {
defer func() { <-p.sem }()
for {
task()
task = <-p.work
}
}
59. 多个goroutine对同一个map写会panic,异常是否可以用defer捕获?
答案: 会panic,可以用defer+recover捕获:
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered:", r)
}
}()
60. 如何实现一个线程安全的计数器?
答案:
type Counter struct {
mu sync.Mutex
value int64
}
func (c *Counter) Increment() {
c.mu.Lock()
defer c.mu.Unlock()
c.value++
}
func (c *Counter) Value() int64 {
c.mu.Lock()
defer c.mu.Unlock()
return c.value
}
61. 什么是原子操作?如何使用sync/atomic包?
答案:
var counter int64
// 原子增加
atomic.AddInt64(&counter, 1)
// 原子加载
value := atomic.LoadInt64(&counter)
// 原子存储
atomic.StoreInt64(&counter, 100)
// 原子交换
old := atomic.SwapInt64(&counter, 200)
62. 如何实现生产者消费者模式?
答案:
func producer(ch chan<- int) {
for i := 0; i < 10; i++ {
ch <- i
}
close(ch)
}
func consumer(ch <-chan int) {
for v := range ch {
fmt.Println("Consumed:", v)
}
}
ch := make(chan int, 5)
go producer(ch)
go consumer(ch)
63. 如何实现扇入扇出模式?
答案:
// 扇出
func fanOut(in <-chan int, out1, out2 chan<- int) {
for v := range in {
select {
case out1 <- v:
case out2 <- v:
}
}
}
// 扇入
func fanIn(in1, in2 <-chan int, out chan<- int) {
for {
select {
case v := <-in1:
out <- v
case v := <-in2:
out <- v
}
}
}
64. 如何实现管道模式?
答案:
func pipeline() {
// 第一阶段:生成数据
numbers := make(chan int)
go func() {
defer close(numbers)
for i := 1; i <= 10; i++ {
numbers <- i
}
}()
// 第二阶段:平方
squares := make(chan int)
go func() {
defer close(squares)
for n := range numbers {
squares <- n * n
}
}()
// 第三阶段:输出
for s := range squares {
fmt.Println(s)
}
}
65. Go协程、Channel通道,被close后,读会带来什么问题?
答案: 读取没问题,但是写入就会有问题:
ch := make(chan int, 1)
close(ch)
// 读取:安全,返回零值和false
v, ok := <-ch // v=0, ok=false
// 写入:panic
ch <- 1 // panic: send on closed channel
模块三:高级特性与性能优化
66. 什么是垃圾回收(GC)?Go的GC算法是什么?
答案: Go使用三色标记清除算法:
- 白色:未被访问的对象
- 灰色:已被访问但其引用的对象未被访问
- 黑色:已被访问且其引用的对象也已被访问
- 最终白色对象被回收
67. 什么是写屏障?Go中有哪些写屏障?
答案: 写屏障是GC期间保证数据一致性的机制:
- Dijkstra写屏障:插入屏障
- Yuasa写屏障:删除屏障
- 混合写屏障:Go 1.8引入,结合了两种屏障的优点
68. 什么是STW(Stop The World)?如何减少STW时间?
答案: STW是GC期间暂停所有goroutine的过程。减少STW时间的方法:
- 减少对象分配
- 使用对象池
- 避免大对象
- 调整GC参数(GOGC)
69. 什么是内存逃逸?如何避免?
答案: 内存逃逸是指本应在栈上分配的变量被分配到堆上。避免方法:
- 避免返回局部变量的指针
- 减少interface{}的使用
- 避免闭包捕获大对象
- 使用值类型而非指针类型
70. 如何分析内存逃逸?
答案:
go build -gcflags="-m" main.go
或者
go run -gcflags="-m -l" main.go
71. 什么是interface{}?它的底层实现是什么?
答案: interface{}是空接口,可以存储任何类型的值。底层实现:
type eface struct {
_type *_type
data unsafe.Pointer
}
72. 接口的动态类型和动态值是什么?
答案:
- 动态类型:接口变量实际存储的值的类型
- 动态值:接口变量实际存储的值
var i interface{} = 42
// 动态类型:int
// 动态值:42
73. 如何判断接口是否为nil?
答案:
var i interface{}
fmt.Println(i == nil) // true
var p *int
i = p
fmt.Println(i == nil) // false,因为动态类型不为nil
74. 什么是类型断言?如何使用?
答案:
var i interface{} = 42
// 类型断言
v, ok := i.(int)
if ok {
fmt.Println("Value:", v)
}
// 类型switch
switch v := i.(type) {
case int:
fmt.Println("Integer:", v)
case string:
fmt.Println("String:", v)
}
75. uintptr和unsafe.Pointer的区别?
答案:
- unsafe.Pointer:通用指针类型,可以转换为任意指针类型
- uintptr:指针的数值表示,可以进行算术运算
- unsafe.Pointer有GC保护,uintptr没有
76. 如何使用unsafe包?有什么风险?
答案:
type Person struct {
Name string
Age int
}
p := Person{"Alice", 30}
namePtr := unsafe.Pointer(&p)
agePtr := unsafe.Pointer(uintptr(namePtr) + unsafe.Offsetof(p.Age))
age := *(*int)(agePtr)
风险:破坏类型安全、可能导致程序崩溃
77. 什么是反射?如何使用reflect包?
答案:
func reflectExample(x interface{}) {
v := reflect.ValueOf(x)
t := reflect.TypeOf(x)
fmt.Println("Type:", t)
fmt.Println("Value:", v)
if v.Kind() == reflect.Struct {
for i := 0; i < v.NumField(); i++ {
field := v.Field(i)
fmt.Printf("Field %d: %v\n", i, field)
}
}
}
78. 反射的性能影响有多大?
答案: 反射比直接调用慢10-100倍,应该避免在性能敏感的代码中使用。
79. 如何获取结构体字段的tag?
答案:
type User struct {
Name string `json:"name" db:"user_name"`
Age int `json:"age" db:"user_age"`
}
func getTags() {
t := reflect.TypeOf(User{})
field, _ := t.FieldByName("Name")
jsonTag := field.Tag.Get("json")
dbTag := field.Tag.Get("db")
}
80. 什么是方法集?
答案: 方法集是类型可以调用的方法的集合:
- 值类型:只能调用值接收者的方法
- 指针类型:可以调用值接收者和指针接收者的方法
81. 值接收者和指针接收者的区别?
答案:
type Counter struct {
count int
}
// 值接收者
func (c Counter) GetCount() int {
return c.count
}
// 指针接收者
func (c *Counter) Increment() {
c.count++
}
82. 如何实现一个泛型函数?(Go 1.18+)
答案:
func Max[T comparable](a, b T) T {
if a > b {
return a
}
return b
}
// 使用
result := Max(10, 20)
result2 := Max("hello", "world")
83. 什么是类型约束?
答案:
type Ordered interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 |
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 |
~float32 | ~float64 | ~string
}
func Sort[T Ordered](slice []T) {
// 排序实现
}
84. 如何优化Go程序的性能?
答案:
- 使用pprof进行性能分析
- 减少内存分配
- 使用对象池
- 避免不必要的类型转换
- 使用更高效的数据结构
- 并发优化
85. 如何使用pprof进行性能分析?
答案:
import _ "net/http/pprof"
func main() {
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
// 应用代码
}
86. 什么是内存对齐?为什么重要?
答案: 内存对齐是指数据在内存中的排列方式。重要性:
- 提高CPU访问效率
- 避免跨缓存行访问
- 某些架构要求对齐访问
87. 如何查看结构体的内存布局?
答案:
type Example struct {
a bool // 1 byte
b int32 // 4 bytes
c bool // 1 byte
}
fmt.Println(unsafe.Sizeof(Example{})) // 12 bytes (due to padding)
fmt.Println(unsafe.Alignof(Example{})) // 4 bytes
88. 什么是零拷贝?Go中如何实现?
答案: 零拷贝是指在数据传输过程中避免不必要的内存拷贝:
- 使用io.Copy
- 使用sendfile系统调用
- 使用mmap
89. 如何减少GC压力?
答案:
- 使用sync.Pool复用对象
- 减少指针的使用
- 使用[]byte而非string
- 预分配slice容量
- 避免频繁的小对象分配
90. 什么是逃逸分析?
答案: 逃逸分析是编译器决定变量分配在栈还是堆的过程:
func createInt() *int {
x := 42
return &x // x逃逸到堆
}
func useInt() {
x := 42 // x分配在栈
fmt.Println(x)
}
91. 如何实现一个高性能的缓存?
答案:
type Cache struct {
mu sync.RWMutex
items map[string]interface{}
}
func (c *Cache) Get(key string) (interface{}, bool) {
c.mu.RLock()
defer c.mu.RUnlock()
value, exists := c.items[key]
return value, exists
}
func (c *Cache) Set(key string, value interface{}) {
c.mu.Lock()
defer c.mu.Unlock()
c.items[key] = value
}
92. 什么是内存池?如何实现?
答案:
type Pool struct {
pool sync.Pool
}
func NewPool() *Pool {
return &Pool{
pool: sync.Pool{
New: func() interface{} {
return make([]byte, 1024)
},
},
}
}
func (p *Pool) Get() []byte {
return p.pool.Get().([]byte)
}
func (p *Pool) Put(buf []byte) {
p.pool.Put(buf)
}
93. 如何避免内存泄漏?
答案:
- 及时关闭资源(文件、连接等)
- 避免循环引用
- 正确使用context
- 避免goroutine泄漏
- 注意slice的容量泄漏
94. 什么是CPU缓存友好的编程?
答案:
- 数据局部性:相关数据放在一起
- 避免false sharing
- 使用数组而非链表
- 顺序访问而非随机访问
95. 如何实现无锁编程?
答案:
type LockFreeQueue struct {
head unsafe.Pointer
tail unsafe.Pointer
}
func (q *LockFreeQueue) Enqueue(value interface{}) {
node := &Node{value: value}
for {
tail := (*Node)(atomic.LoadPointer(&q.tail))
next := (*Node)(atomic.LoadPointer(&tail.next))
if tail == (*Node)(atomic.LoadPointer(&q.tail)) {
if next == nil {
if atomic.CompareAndSwapPointer(&tail.next, nil, unsafe.Pointer(node)) {
atomic.CompareAndSwapPointer(&q.tail, unsafe.Pointer(tail), unsafe.Pointer(node))
break
}
} else {
atomic.CompareAndSwapPointer(&q.tail, unsafe.Pointer(tail), unsafe.Pointer(next))
}
}
}
}
96. 什么是内存屏障?
答案: 内存屏障是防止CPU重排序的机制,确保内存操作的顺序性。Go中通过sync/atomic包提供内存屏障功能。
97. 如何实现高性能的字符串拼接?
答案:
// 使用strings.Builder
var builder strings.Builder
builder.WriteString("hello")
builder.WriteString(" ")
builder.WriteString("world")
result := builder.String()
// 使用bytes.Buffer
var buffer bytes.Buffer
buffer.WriteString("hello")
buffer.WriteString(" ")
buffer.WriteString("world")
result := buffer.String()
98. 什么是编译器优化?Go有哪些优化?
答案: Go编译器的优化包括:
- 内联优化
- 逃逸分析
- 死代码消除
- 常量折叠
- 边界检查消除
99. 如何进行基准测试?
答案:
func BenchmarkStringConcat(b *testing.B) {
for i := 0; i < b.N; i++ {
result := "hello" + " " + "world"
_ = result
}
}
func BenchmarkStringBuilder(b *testing.B) {
for i := 0; i < b.N; i++ {
var builder strings.Builder
builder.WriteString("hello")
builder.WriteString(" ")
builder.WriteString("world")
_ = builder.String()
}
}
100. 如何分析程序的内存使用?
答案:
# 生成内存profile
go tool pprof http://localhost:6060/debug/pprof/heap
# 分析内存分配
go tool pprof http://localhost:6060/debug/pprof/allocs
# 查看GC信息
GODEBUG=gctrace=1 go run main.go
模块四:标准库与工程实践
101. 如何处理错误?Go的错误处理机制是什么?
答案: Go使用显式错误处理:
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
result, err := divide(10, 0)
if err != nil {
log.Fatal(err)
}
102. 什么是panic和recover?如何使用?
答案:
func safeDivide(a, b int) (result int, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic recovered: %v", r)
}
}()
if b == 0 {
panic("division by zero")
}
return a / b, nil
}
103. 如何自定义错误类型?
答案:
type ValidationError struct {
Field string
Message string
}
func (e ValidationError) Error() string {
return fmt.Sprintf("validation failed for field %s: %s", e.Field, e.Message)
}
func validate(name string) error {
if name == "" {
return ValidationError{
Field: "name",
Message: "cannot be empty",
}
}
return nil
}
104. 什么是defer语句?执行顺序是什么?
答案: defer语句延迟执行,遵循LIFO(后进先出)顺序:
func example() {
defer fmt.Println("1")
defer fmt.Println("2")
defer fmt.Println("3")
fmt.Println("4")
}
// 输出:4 3 2 1
105. defer语句的参数何时求值?
答案: defer语句的参数在defer语句执行时求值:
func example() {
i := 0
defer fmt.Println(i) // 输出0,不是1
i++
}
106. 如何进行单元测试?
答案:
// math.go
func Add(a, b int) int {
return a + b
}
// math_test.go
func TestAdd(t *testing.T) {
result := Add(2, 3)
expected := 5
if result != expected {
t.Errorf("Add(2, 3) = %d; want %d", result, expected)
}
}
107. 如何进行表格驱动测试?
答案:
func TestAdd(t *testing.T) {
tests := []struct {
name string
a, b int
expected int
}{
{"positive numbers", 2, 3, 5},
{"negative numbers", -2, -3, -5},
{"zero", 0, 5, 5},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := Add(tt.a, tt.b)
if result != tt.expected {
t.Errorf("Add(%d, %d) = %d; want %d", tt.a, tt.b, result, tt.expected)
}
})
}
}
108. 如何进行基准测试?
答案:
func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
Add(2, 3)
}
}
// 运行:go test -bench=.
109. 如何使用testify库?
答案:
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestAdd(t *testing.T) {
result := Add(2, 3)
assert.Equal(t, 5, result)
assert.NotNil(t, result)
}
110. 什么是go mod?如何使用?
答案:
# 初始化模块
go mod init example.com/myproject
# 添加依赖
go get github.com/gin-gonic/gin
# 更新依赖
go get -u github.com/gin-gonic/gin
# 清理未使用的依赖
go mod tidy
111. go.mod和go.sum文件的作用?
答案:
- go.mod:定义模块路径和依赖版本
- go.sum:包含依赖模块的校验和,确保依赖完整性
112. 如何处理依赖版本冲突?
答案:
# 查看依赖图
go mod graph
# 查看为什么需要某个依赖
go mod why github.com/some/package
# 替换依赖
go mod edit -replace github.com/old/package=github.com/new/package@v1.0.0
113. 如何读写文件?
答案:
// 读文件
content, err := ioutil.ReadFile("file.txt")
if err != nil {
log.Fatal(err)
}
// 写文件
err = ioutil.WriteFile("file.txt", []byte("Hello World"), 0644)
if err != nil {
log.Fatal(err)
}
// 使用os包
file, err := os.Open("file.txt")
if err != nil {
log.Fatal(err)
}
defer file.Close()
114. 如何处理JSON数据?
答案:
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
// 序列化
p := Person{Name: "Alice", Age: 30}
data, err := json.Marshal(p)
// 反序列化
var p2 Person
err = json.Unmarshal(data, &p2)
115. 如何处理时间和日期?
答案:
// 当前时间
now := time.Now()
// 解析时间
t, err := time.Parse("2006-01-02 15:04:05", "2023-01-01 12:00:00")
// 格式化时间
formatted := now.Format("2006-01-02 15:04:05")
// 时间计算
tomorrow := now.Add(24 * time.Hour)
116. 如何使用正则表达式?
答案:
import "regexp"
// 编译正则表达式
re := regexp.MustCompile(`\d+`)
// 查找匹配
matches := re.FindAllString("abc123def456", -1)
// 替换
result := re.ReplaceAllString("abc123def456", "X")
117. 如何进行HTTP客户端请求?
答案:
// GET请求
resp, err := http.Get("https://api.example.com/users")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
// POST请求
data := strings.NewReader(`{"name":"Alice"}`)
resp, err = http.Post("https://api.example.com/users", "application/json", data)
118. 如何创建HTTP服务器?
答案:
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World!")
}
func main() {
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
119. 如何使用context包?
答案:
// 带超时的context
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// 带取消的context
ctx, cancel := context.WithCancel(context.Background())
// 传递值
ctx = context.WithValue(ctx, "userID", 123)
userID := ctx.Value("userID").(int)
120. 如何处理命令行参数?
答案:
import "flag"
var (
name = flag.String("name", "World", "name to greet")
age = flag.Int("age", 0, "age of person")
)
func main() {
flag.Parse()
fmt.Printf("Hello, %s! You are %d years old.\n", *name, *age)
}
121. 如何使用log包?
答案:
import "log"
// 基本日志
log.Println("This is a log message")
// 设置日志格式
log.SetFlags(log.LstdFlags | log.Lshortfile)
// 自定义logger
logger := log.New(os.Stdout, "PREFIX: ", log.LstdFlags)
logger.Println("Custom log message")
122. 如何处理环境变量?
答案:
import "os"
// 获取环境变量
value := os.Getenv("HOME")
// 设置环境变量
os.Setenv("MY_VAR", "my_value")
// 获取所有环境变量
for _, env := range os.Environ() {
fmt.Println(env)
}
123. 如何使用strings包?
答案:
import "strings"
// 字符串操作
s := "Hello, World!"
fmt.Println(strings.ToUpper(s))
fmt.Println(strings.Contains(s, "World"))
fmt.Println(strings.Split(s, ","))
fmt.Println(strings.Join([]string{"a", "b", "c"}, "-"))
124. 如何使用strconv包?
答案:
import "strconv"
// 字符串转数字
i, err := strconv.Atoi("123")
f, err := strconv.ParseFloat("3.14", 64)
// 数字转字符串
s := strconv.Itoa(123)
s = strconv.FormatFloat(3.14, 'f', 2, 64)
125. 如何使用sort包?
答案:
import "sort"
// 排序切片
ints := []int{3, 1, 4, 1, 5}
sort.Ints(ints)
strings := []string{"banana", "apple", "cherry"}
sort.Strings(strings)
// 自定义排序
sort.Slice(ints, func(i, j int) bool {
return ints[i] > ints[j] // 降序
})
126. 如何使用fmt包进行格式化?
答案:
// 基本格式化
fmt.Printf("Name: %s, Age: %d\n", "Alice", 30)
// 格式化到字符串
s := fmt.Sprintf("Value: %v", 42)
// 不同的格式化动词
fmt.Printf("%d %o %x %X\n", 42, 42, 42, 42) // 十进制、八进制、十六进制
fmt.Printf("%f %e %g\n", 3.14, 3.14, 3.14) // 浮点数格式
127. 如何处理路径和文件名?
答案:
import "path/filepath"
// 路径操作
dir := filepath.Dir("/path/to/file.txt") // /path/to
base := filepath.Base("/path/to/file.txt") // file.txt
ext := filepath.Ext("/path/to/file.txt") // .txt
// 路径拼接
path := filepath.Join("path", "to", "file.txt")
// 绝对路径
abs, err := filepath.Abs("./file.txt")
128. 如何使用bufio包?
答案:
import "bufio"
// 读取文件
file, err := os.Open("file.txt")
if err != nil {
log.Fatal(err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
// 写入文件
writer := bufio.NewWriter(file)
writer.WriteString("Hello, World!")
writer.Flush()
129. 如何使用crypto包进行加密?
答案:
import (
"crypto/md5"
"crypto/sha256"
"fmt"
)
// MD5哈希
data := []byte("hello world")
hash := md5.Sum(data)
fmt.Printf("%x\n", hash)
// SHA256哈希
hash256 := sha256.Sum256(data)
fmt.Printf("%x\n", hash256)
130. 如何进行代码覆盖率测试?
答案:
# 运行测试并生成覆盖率报告
go test -cover
# 生成详细的覆盖率报告
go test -coverprofile=coverage.out
go tool cover -html=coverage.out -o coverage.html
131. 如何使用build tags?
答案:
// +build linux
package main
import "fmt"
func main() {
fmt.Println("This only compiles on Linux")
}
132. 如何进行性能分析?
答案:
import _ "net/http/pprof"
func main() {
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
// 应用代码
}
// 访问 http://localhost:6060/debug/pprof/
133. 如何使用vendor目录?
答案:
# 创建vendor目录
go mod vendor
# 使用vendor目录构建
go build -mod=vendor
134. 如何进行交叉编译?
答案:
# 编译Linux版本
GOOS=linux GOARCH=amd64 go build -o myapp-linux
# 编译Windows版本
GOOS=windows GOARCH=amd64 go build -o myapp.exe
# 编译macOS版本
GOOS=darwin GOARCH=amd64 go build -o myapp-mac
135. 如何优化编译速度?
答案:
- 使用go build cache
- 减少不必要的依赖
- 使用并行编译
- 使用增量编译
模块五:算法与编程实战
136. 实现冒泡排序
答案:
func bubbleSort(arr []int) {
n := len(arr)
for i := 0; i < n-1; i++ {
for j := 0; j < n-i-1; j++ {
if arr[j] > arr[j+1] {
arr[j], arr[j+1] = arr[j+1], arr[j]
}
}
}
}
137. 实现快速排序
答案:
func quickSort(arr []int, low, high int) {
if low < high {
pi := partition(arr, low, high)
quickSort(arr, low, pi-1)
quickSort(arr, pi+1, high)
}
}
func partition(arr []int, low, high int) int {
pivot := arr[high]
i := low - 1
for j := low; j < high; j++ {
if arr[j] < pivot {
i++
arr[i], arr[j] = arr[j], arr[i]
}
}
arr[i+1], arr[high] = arr[high], arr[i+1]
return i + 1
}
138. 实现二分查找
答案:
func binarySearch(arr []int, target int) int {
left, right := 0, len(arr)-1
for left <= right {
mid := left + (right-left)/2
if arr[mid] == target {
return mid
} else if arr[mid] < target {
left = mid + 1
} else {
right = mid - 1
}
}
return -1
}
139. 反转链表
答案:
type ListNode struct {
Val int
Next *ListNode
}
func reverseList(head *ListNode) *ListNode {
var prev *ListNode
current := head
for current != nil {
next := current.Next
current.Next = prev
prev = current
current = next
}
return prev
}
140. 检测链表是否有环
答案:
func hasCycle(head *ListNode) bool {
if head == nil || head.Next == nil {
return false
}
slow := head
fast := head.Next
for slow != fast {
if fast == nil || fast.Next == nil {
return false
}
slow = slow.Next
fast = fast.Next.Next
}
return true
}
141. 合并两个有序链表
答案:
func mergeTwoLists(l1 *ListNode, l2 *ListNode) *ListNode {
dummy := &ListNode{}
current := dummy
for l1 != nil && l2 != nil {
if l1.Val <= l2.Val {
current.Next = l1
l1 = l1.Next
} else {
current.Next = l2
l2 = l2.Next
}
current = current.Next
}
if l1 != nil {
current.Next = l1
} else {
current.Next = l2
}
return dummy.Next
}
142. 实现栈
答案:
type Stack struct {
items []int
}
func (s *Stack) Push(item int) {
s.items = append(s.items, item)
}
func (s *Stack) Pop() int {
if len(s.items) == 0 {
return -1 // 或者panic
}
index := len(s.items) - 1
item := s.items[index]
s.items = s.items[:index]
return item
}
func (s *Stack) IsEmpty() bool {
return len(s.items) == 0
}
func (s *Stack) Top() int {
if len(s.items) == 0 {
return -1
}
return s.items[len(s.items)-1]
}
143. 实现队列
答案:
type Queue struct {
items []int
}
func (q *Queue) Enqueue(item int) {
q.items = append(q.items, item)
}
func (q *Queue) Dequeue() int {
if len(q.items) == 0 {
return -1
}
item := q.items[0]
q.items = q.items[1:]
return item
}
func (q *Queue) IsEmpty() bool {
return len(q.items) == 0
}
func (q *Queue) Front() int {
if len(q.items) == 0 {
return -1
}
return q.items[0]
}
144. 括号匹配检查
答案:
func isValid(s string) bool {
stack := []rune{}
mapping := map[rune]rune{
')': '(',
'}': '{',
']': '[',
}
for _, char := range s {
if char == '(' || char == '{' || char == '[' {
stack = append(stack, char)
} else if char == ')' || char == '}' || char == ']' {
if len(stack) == 0 || stack[len(stack)-1] != mapping[char] {
return false
}
stack = stack[:len(stack)-1]
}
}
return len(stack) == 0
}
145. 两数之和
答案:
func twoSum(nums []int, target int) []int {
numMap := make(map[int]int)
for i, num := range nums {
complement := target - num
if index, exists := numMap[complement]; exists {
return []int{index, i}
}
numMap[num] = i
}
return nil
}
146. 最长公共前缀
答案:
func longestCommonPrefix(strs []string) string {
if len(strs) == 0 {
return ""
}
prefix := strs[0]
for i := 1; i < len(strs); i++ {
for !strings.HasPrefix(strs[i], prefix) {
prefix = prefix[:len(prefix)-1]
if prefix == "" {
return ""
}
}
}
return prefix
}
147. 斐波那契数列
答案:
// 递归版本
func fibonacci(n int) int {
if n <= 1 {
return n
}
return fibonacci(n-1) + fibonacci(n-2)
}
// 迭代版本
func fibonacciIterative(n int) int {
if n <= 1 {
return n
}
a, b := 0, 1
for i := 2; i <= n; i++ {
a, b = b, a+b
}
return b
}
148. 字符串反转
答案:
func reverseString(s string) string {
runes := []rune(s)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes)
}
149. 判断回文字符串
答案:
func isPalindrome(s string) bool {
s = strings.ToLower(s)
left, right := 0, len(s)-1
for left < right {
// 跳过非字母数字字符
for left < right && !isAlphanumeric(s[left]) {
left++
}
for left < right && !isAlphanumeric(s[right]) {
right--
}
if s[left] != s[right] {
return false
}
left++
right--
}
return true
}
func isAlphanumeric(c byte) bool {
return (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')
}
150. 数组去重
答案:
func removeDuplicates(nums []int) []int {
if len(nums) == 0 {
return nums
}
seen := make(map[int]bool)
result := []int{}
for _, num := range nums {
if !seen[num] {
seen[num] = true
result = append(result, num)
}
}
return result
}
151. 找出数组中的最大值和最小值
答案:
func findMinMax(arr []int) (int, int) {
if len(arr) == 0 {
return 0, 0
}
min, max := arr[0], arr[0]
for _, num := range arr[1:] {
if num < min {
min = num
}
if num > max {
max = num
}
}
return min, max
}
152. 实现LRU缓存
答案:
type LRUCache struct {
capacity int
cache map[int]*Node
head *Node
tail *Node
}
type Node struct {
key int
value int
prev *Node
next *Node
}
func Constructor(capacity int) LRUCache {
head := &Node{}
tail := &Node{}
head.next = tail
tail.prev = head
return LRUCache{
capacity: capacity,
cache: make(map[int]*Node),
head: head,
tail: tail,
}
}
func (lru *LRUCache) Get(key int) int {
if node, exists := lru.cache[key]; exists {
lru.moveToHead(node)
return node.value
}
return -1
}
func (lru *LRUCache) Put(key int, value int) {
if node, exists := lru.cache[key]; exists {
node.value = value
lru.moveToHead(node)
} else {
newNode := &Node{key: key, value: value}
lru.cache[key] = newNode
lru.addToHead(newNode)
if len(lru.cache) > lru.capacity {
tail := lru.removeTail()
delete(lru.cache, tail.key)
}
}
}
153. 二叉树的前序遍历
答案:
type TreeNode struct {
Val int
Left *TreeNode
Right *TreeNode
}
func preorderTraversal(root *TreeNode) []int {
var result []int
if root == nil {
return result
}
result = append(result, root.Val)
result = append(result, preorderTraversal(root.Left)...)
result = append(result, preorderTraversal(root.Right)...)
return result
}
154. 二叉树的层序遍历
答案:
func levelOrder(root *TreeNode) [][]int {
if root == nil {
return [][]int{}
}
var result [][]int
queue := []*TreeNode{root}
for len(queue) > 0 {
levelSize := len(queue)
var level []int
for i := 0; i < levelSize; i++ {
node := queue[0]
queue = queue[1:]
level = append(level, node.Val)
if node.Left != nil {
queue = append(queue, node.Left)
}
if node.Right != nil {
queue = append(queue, node.Right)
}
}
result = append(result, level)
}
return result
}
155. 计算二叉树的最大深度
答案:
func maxDepth(root *TreeNode) int {
if root == nil {
return 0
}
leftDepth := maxDepth(root.Left)
rightDepth := maxDepth(root.Right)
if leftDepth > rightDepth {
return leftDepth + 1
}
return rightDepth + 1
}
156. 数组中零元素移到末尾
答案:
func moveZeroes(nums []int) {
writeIndex := 0
// 将非零元素移到前面
for readIndex := 0; readIndex < len(nums); readIndex++ {
if nums[readIndex] != 0 {
nums[writeIndex] = nums[readIndex]
writeIndex++
}
}
// 将剩余位置填充为0
for writeIndex < len(nums) {
nums[writeIndex] = 0
writeIndex++
}
}
157. 旋转数组
答案:
func rotate(nums []int, k int) {
n := len(nums)
k = k % n
// 反转整个数组
reverse(nums, 0, n-1)
// 反转前k个元素
reverse(nums, 0, k-1)
// 反转后n-k个元素
reverse(nums, k, n-1)
}
func reverse(nums []int, start, end int) {
for start < end {
nums[start], nums[end] = nums[end], nums[start]
start++
end--
}
}
158. 寻找数组中的重复数字
答案:
func findDuplicate(nums []int) int {
// 使用快慢指针(Floyd判圈算法)
slow := nums[0]
fast := nums[0]
// 第一阶段:寻找环
for {
slow = nums[slow]
fast = nums[nums[fast]]
if slow == fast {
break
}
}
// 第二阶段:寻找环的入口
slow = nums[0]
for slow != fast {
slow = nums[slow]
fast = nums[fast]
}
return slow
}
159. 实现字符串匹配(KMP算法)
答案:
func strStr(haystack string, needle string) int {
if len(needle) == 0 {
return 0
}
// 构建部分匹配表
lps := buildLPS(needle)
i, j := 0, 0
for i < len(haystack) {
if haystack[i] == needle[j] {
i++
j++
}
if j == len(needle) {
return i - j
} else if i < len(haystack) && haystack[i] != needle[j] {
if j != 0 {
j = lps[j-1]
} else {
i++
}
}
}
return -1
}
func buildLPS(pattern string) []int {
lps := make([]int, len(pattern))
length := 0
i := 1
for i < len(pattern) {
if pattern[i] == pattern[length] {
length++
lps[i] = length
i++
} else {
if length != 0 {
length = lps[length-1]
} else {
lps[i] = 0
i++
}
}
}
return lps
}
160. 实现并发安全的计数器
答案:
type SafeCounter struct {
mu sync.Mutex
value int
}
func (c *SafeCounter) Increment() {
c.mu.Lock()
defer c.mu.Unlock()
c.value++
}
func (c *SafeCounter) Decrement() {
c.mu.Lock()
defer c.mu.Unlock()
c.value--
}
func (c *SafeCounter) Value() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.value
}
161. 实现生产者消费者模式
答案:
func producerConsumer() {
buffer := make(chan int, 10)
// 生产者
go func() {
for i := 0; i < 20; i++ {
buffer <- i
fmt.Printf("Produced: %d\n", i)
time.Sleep(100 * time.Millisecond)
}
close(buffer)
}()
// 消费者
go func() {
for item := range buffer {
fmt.Printf("Consumed: %d\n", item)
time.Sleep(200 * time.Millisecond)
}
}()
}
162. 实现限流器
答案:
type RateLimiter struct {
tokens chan struct{}
ticker *time.Ticker
}
func NewRateLimiter(rate int) *RateLimiter {
rl := &RateLimiter{
tokens: make(chan struct{}, rate),
ticker: time.NewTicker(time.Second / time.Duration(rate)),
}
// 初始化令牌
for i := 0; i < rate; i++ {
rl.tokens <- struct{}{}
}
// 定期添加令牌
go func() {
for range rl.ticker.C {
select {
case rl.tokens <- struct{}{}:
default:
}
}
}()
return rl
}
func (rl *RateLimiter) Allow() bool {
select {
case <-rl.tokens:
return true
default:
return false
}
}
163. 实现工作池
答案:
type WorkerPool struct {
workerCount int
jobQueue chan Job
workers []Worker
}
type Job func()
type Worker struct {
id int
jobQueue chan Job
quit chan bool
}
func NewWorkerPool(workerCount int, jobQueueSize int) *WorkerPool {
return &WorkerPool{
workerCount: workerCount,
jobQueue: make(chan Job, jobQueueSize),
workers: make([]Worker, workerCount),
}
}
func (wp *WorkerPool) Start() {
for i := 0; i < wp.workerCount; i++ {
worker := Worker{
id: i,
jobQueue: wp.jobQueue,
quit: make(chan bool),
}
wp.workers[i] = worker
go worker.start()
}
}
func (w *Worker) start() {
for {
select {
case job := <-w.jobQueue:
job()
case <-w.quit:
return
}
}
}
164. 实现分布式锁
答案:
type DistributedLock struct {
key string
value string
expiry time.Duration
client *redis.Client
}
func NewDistributedLock(client *redis.Client, key string, expiry time.Duration) *DistributedLock {
return &DistributedLock{
key: key,
value: generateUniqueValue(),
expiry: expiry,
client: client,
}
}
func (dl *DistributedLock) Lock() bool {
result := dl.client.SetNX(dl.key, dl.value, dl.expiry)
return result.Val()
}
func (dl *DistributedLock) Unlock() bool {
script := `
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
`
result := dl.client.Eval(script, []string{dl.key}, dl.value)
return result.Val().(int64) == 1
}
165. 实现一致性哈希
答案:
type ConsistentHash struct {
hashRing map[uint32]string
nodes []uint32
replicas int
}
func NewConsistentHash(replicas int) *ConsistentHash {
return &ConsistentHash{
hashRing: make(map[uint32]string),
replicas: replicas,
}
}
func (ch *ConsistentHash) AddNode(node string) {
for i := 0; i < ch.replicas; i++ {
hash := ch.hash(fmt.Sprintf("%s:%d", node, i))
ch.nodes = append(ch.nodes, hash)
ch.hashRing[hash] = node
}
sort.Slice(ch.nodes, func(i, j int) bool {
return ch.nodes[i] < ch.nodes[j]
})
}
func (ch *ConsistentHash) GetNode(key string) string {
if len(ch.nodes) == 0 {
return ""
}
hash := ch.hash(key)
idx := sort.Search(len(ch.nodes), func(i int) bool {
return ch.nodes[i] >= hash
})
if idx == len(ch.nodes) {
idx = 0
}
return ch.hashRing[ch.nodes[idx]]
}
func (ch *ConsistentHash) hash(key string) uint32 {
h := crc32.NewIEEE()
h.Write([]byte(key))
return h.Sum32()
}
总结
本文档包含了165道Go语言面试题,涵盖了以下5个核心模块:
- Go语言基础与数据类型(35题):包括基本语法、数据类型、内存管理等基础知识
- 并发编程与Goroutine(30题):涵盖goroutine、channel、同步原语等并发编程核心概念
- 高级特性与性能优化(35题):包括GC、反射、接口、性能优化等高级特性
- 标准库与工程实践(35题):涵盖标准库使用、测试、包管理等工程实践
- 算法与编程实战(30题):包括常见算法、数据结构、系统设计等实战题目
这些面试题从基础到高级,从理论到实践,全面覆盖了Go语言开发者需要掌握的核心知识点,适合不同水平的开发者学习和面试准备。