面试题答案
一键面试- 异常对并发安全性的影响:
- 当一个goroutine通过
recover
捕获到异常时,该goroutine内部的局部状态可能会因为异常处理而改变。如果共享资源的状态依赖于这个goroutine的正常执行流程,那么异常处理可能会导致共享资源处于不一致状态。 - 其他正在读写共享资源的goroutine可能会读到这个不一致的状态,从而破坏并发安全性。例如,假设共享资源是一个计数器,正常情况下应该递增,但在异常处理中可能会被重置或者没有正确递增,其他goroutine读取到的计数器值就可能不正确。
- 当一个goroutine通过
- 确保异常处理后程序保持并发安全的方法:
- 使用
sync.Mutex
:- 在对共享资源进行读写操作前,先获取锁。在异常处理后,确保释放锁。例如:
- 使用
package main
import (
"fmt"
"sync"
)
var (
sharedResource int
mu sync.Mutex
)
func worker(wg *sync.WaitGroup) {
defer wg.Done()
mu.Lock()
defer mu.Unlock()
// 模拟可能发生异常的操作
if sharedResource < 0 {
panic("negative value not allowed")
}
sharedResource++
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go worker(&wg)
}
wg.Wait()
fmt.Println("Final sharedResource:", sharedResource)
}
- 使用
sync.RWMutex
:- 如果读操作远多于写操作,可以使用读写锁。在写操作(可能发生异常的操作)时获取写锁,读操作时获取读锁。例如:
package main
import (
"fmt"
"sync"
)
var (
sharedResource int
rwmu sync.RWMutex
)
func reader(wg *sync.WaitGroup) {
defer wg.Done()
rwmu.RLock()
defer rwmu.RUnlock()
fmt.Println("Reader reads:", sharedResource)
}
func writer(wg *sync.WaitGroup) {
defer wg.Done()
rwmu.Lock()
defer rwmu.Unlock()
// 模拟可能发生异常的操作
if sharedResource < 0 {
panic("negative value not allowed")
}
sharedResource++
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go reader(&wg)
}
for i := 0; i < 3; i++ {
wg.Add(1)
go writer(&wg)
}
wg.Wait()
fmt.Println("Final sharedResource:", sharedResource)
}
- 使用
sync.Cond
:- 当需要基于共享资源的状态进行条件等待和唤醒时,可以结合
sync.Mutex
和sync.Cond
。在异常处理后,需要正确地通知其他等待的goroutine,确保共享资源状态变化后能被及时感知。例如:
- 当需要基于共享资源的状态进行条件等待和唤醒时,可以结合
package main
import (
"fmt"
"sync"
"time"
)
var (
sharedResource int
mu sync.Mutex
cond = sync.NewCond(&mu)
)
func waiter(wg *sync.WaitGroup) {
defer wg.Done()
mu.Lock()
for sharedResource < 10 {
cond.Wait()
}
fmt.Println("Waiter sees:", sharedResource)
mu.Unlock()
}
func changer(wg *sync.WaitGroup) {
defer wg.Done()
mu.Lock()
// 模拟可能发生异常的操作
if sharedResource < 0 {
panic("negative value not allowed")
}
sharedResource++
if sharedResource == 10 {
cond.Broadcast()
}
mu.Unlock()
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 3; i++ {
wg.Add(1)
go waiter(&wg)
}
for i := 0; i < 10; i++ {
wg.Add(1)
go changer(&wg)
}
time.Sleep(2 * time.Second)
fmt.Println("Final sharedResource:", sharedResource)
}
通过这些sync
包的工具,可以在异常处理后仍然保持程序的并发安全。在异常处理时,要确保锁的正确获取和释放,以及条件变量的正确通知,这样才能保证共享资源的状态在并发环境下是一致和安全的。