面试题答案
一键面试1. Mutex锁
- 原理:Mutex(互斥锁)通过限制同一时间只有一个goroutine能够访问共享资源,实现互斥访问。当一个goroutine获取了Mutex锁,其他goroutine必须等待锁被释放才能获取。
- 适用场景:适用于读写操作都可能修改共享资源的场景,保证数据一致性。例如,对银行账户余额的修改操作。
- 性能:由于同一时间只有一个goroutine能获取锁,在高并发写操作场景下,可能会导致性能瓶颈,因为其他goroutine需要等待锁的释放。
示例代码:
package main
import (
"fmt"
"sync"
)
var (
mu sync.Mutex
count int
)
func increment(wg *sync.WaitGroup) {
mu.Lock()
count++
mu.Unlock()
wg.Done()
}
2. 读写锁(RWMutex)
- 原理:读写锁允许同时有多个读操作,但只允许一个写操作。读操作不修改数据,所以可以并发执行;写操作会修改数据,需要独占访问。
- 适用场景:适用于读多写少的场景,例如缓存的读取和更新,大量读操作可以并行进行,而写操作时需要独占锁以保证数据一致性。
- 性能:在读写比高的场景下,性能优于Mutex锁。因为读操作可以并发执行,减少了等待时间。但写操作时仍然独占资源,会影响其他读写操作。
示例代码:
package main
import (
"fmt"
"sync"
)
var (
rwmu sync.RWMutex
value int
)
func read(wg *sync.WaitGroup) {
rwmu.RLock()
fmt.Println("Read value:", value)
rwmu.RUnlock()
wg.Done()
}
func write(wg *sync.WaitGroup) {
rwmu.Lock()
value++
fmt.Println("Write value:", value)
rwmu.Unlock()
wg.Done()
}
3. 通道(channel)
- 原理:通道是Go语言中用于goroutine之间通信的机制,通过发送和接收数据来同步goroutine的执行。它可以实现数据的传递和同步。
- 适用场景:适用于需要在goroutine之间传递数据并同步操作的场景。例如,生产者 - 消费者模型,生产者将数据发送到通道,消费者从通道接收数据。
- 性能:通道在传递数据时会涉及数据的拷贝,在数据量较大时可能有性能开销。但在协调goroutine之间的同步方面非常高效,能避免共享资源的竞争问题。
示例代码:
package main
import (
"fmt"
)
func producer(ch chan int) {
for i := 0; i < 5; i++ {
ch <- i
}
close(ch)
}
func consumer(ch chan int) {
for val := range ch {
fmt.Println("Consumed:", val)
}
}
4. 复杂并发编程场景下的选择
- 读多写少场景:优先选择读写锁(RWMutex),以提高读操作的并发性能,同时保证写操作的数据一致性。
- 读写操作频繁且数据修改频繁场景:使用Mutex锁,虽然性能可能稍低,但能简单有效地保证数据一致性。
- 需要在goroutine之间传递数据并同步场景:选择通道(channel),通过数据传递实现同步,避免共享资源竞争。例如,在微服务架构中不同服务间的数据传递和同步。
总之,在复杂的并发编程场景中,需要根据具体的业务需求和操作特点,综合考虑原理、适用场景和性能等因素,选择最合适的同步机制。