面试题答案
一键面试并发环境下Go方法调用面临的问题及解决办法
- 资源竞争:多个goroutine同时访问和修改共享资源时,可能导致数据不一致。例如多个goroutine同时对一个全局变量进行增减操作。
- 解决办法:使用互斥锁(
sync.Mutex
),在访问共享资源前加锁,访问结束后解锁。示例代码如下:
- 解决办法:使用互斥锁(
package main
import (
"fmt"
"sync"
)
var (
count int
mu sync.Mutex
)
func increment(wg *sync.WaitGroup) {
defer wg.Done()
mu.Lock()
count++
mu.Unlock()
}
- 死锁:当两个或多个goroutine相互等待对方释放资源时,就会发生死锁。例如goroutine A持有锁1并等待锁2,而goroutine B持有锁2并等待锁1。
- 解决办法:合理设计锁的获取顺序,避免循环依赖。例如按照固定顺序获取锁,或者使用
context
来设置操作的截止时间,防止无限期等待。
- 解决办法:合理设计锁的获取顺序,避免循环依赖。例如按照固定顺序获取锁,或者使用
Go语言中方法集与接口实现之间的关系
- 接口实现依赖方法集:在Go语言中,一个类型实现某个接口,是通过该类型的方法集包含接口定义的所有方法来确定的。例如,定义一个
Writer
接口:
type Writer interface {
Write(p []byte) (n int, err error)
}
如果一个类型File
的方法集包含Write
方法,那么File
类型就实现了Writer
接口:
type File struct{}
func (f File) Write(p []byte) (n int, err error) {
// 实现具体逻辑
return len(p), nil
}
- 方法集决定接口实现的有效性:方法集是接口实现的判断依据,只有当类型的方法集完全满足接口方法定义时,才认为该类型实现了此接口。而且Go语言的接口实现是隐式的,不需要显式声明。
方法集在方法调用时的具体作用机制
- 指针接收器与值接收器的区别:方法集与接收器类型有关。对于值接收器的方法,方法集同时属于值类型和指针类型;对于指针接收器的方法,方法集只属于指针类型。例如:
type User struct {
Name string
}
func (u User) GetName() string {
return u.Name
}
func (u *User) SetName(name string) {
u.Name = name
}
这里User
类型的值和指针都可以调用GetName
方法,但只有User
类型的指针可以调用SetName
方法。
2. 动态类型与方法集:在接口调用中,实际调用的方法由接口值的动态类型的方法集决定。例如:
type Animal interface {
Speak() string
}
type Dog struct{}
func (d Dog) Speak() string {
return "Woof"
}
type Cat struct{}
func (c Cat) Speak() string {
return "Meow"
}
func MakeSound(a Animal) {
fmt.Println(a.Speak())
}
当调用MakeSound
时,实际调用的Speak
方法是由传入的Animal
接口值的动态类型(Dog
或Cat
)的方法集决定的。