面试题答案
一键面试利用闭包实现多个goroutine之间的数据共享
在Go语言中,可以通过闭包来捕获外部变量,使得多个goroutine可以访问和修改这些共享数据。例如:
package main
import (
"fmt"
)
func main() {
sharedData := 0
var routines []func()
for i := 0; i < 5; i++ {
routine := func() {
sharedData++
fmt.Println("goroutine ", i, " incremented sharedData to ", sharedData)
}
routines = append(routines, routine)
}
for _, routine := range routines {
go routine()
}
// 为了让所有goroutine有时间执行,这里简单睡眠一下
select {}
}
在上述代码中,sharedData
是外部变量,闭包函数 routine
捕获了 sharedData
,使得每个goroutine都可以访问和修改这个共享数据。
实现过程中可能遇到的问题
- 数据竞争问题:当多个goroutine同时访问和修改共享数据时,会发生数据竞争。在上述代码中,如果运行,会发现输出的结果可能不是预期的顺序,并且最终的
sharedData
值可能不正确。这是因为多个goroutine同时对sharedData
进行自增操作,没有同步机制,导致数据竞争。
解决方案
- 使用互斥锁(Mutex):通过在访问共享数据前加锁,访问完后解锁,保证同一时间只有一个goroutine可以访问共享数据。
package main
import (
"fmt"
"sync"
)
func main() {
var mu sync.Mutex
sharedData := 0
var routines []func()
for i := 0; i < 5; i++ {
routine := func() {
mu.Lock()
sharedData++
fmt.Println("goroutine ", i, " incremented sharedData to ", sharedData)
mu.Unlock()
}
routines = append(routines, routine)
}
for _, routine := range routines {
go routine()
}
// 为了让所有goroutine有时间执行,这里简单睡眠一下
select {}
}
- 使用通道(Channel):可以将对共享数据的操作通过通道发送给一个专门处理这些操作的goroutine,由这个goroutine来保证数据的一致性。
package main
import (
"fmt"
)
func main() {
sharedData := 0
operationCh := make(chan func())
var wg sync.WaitGroup
wg.Add(5)
go func() {
for op := range operationCh {
op()
wg.Done()
}
}()
for i := 0; i < 5; i++ {
operationCh <- func() {
sharedData++
fmt.Println("goroutine ", i, " incremented sharedData to ", sharedData)
}
}
close(operationCh)
wg.Wait()
}
通过上述方式,可以有效地解决在使用闭包实现多个goroutine之间数据共享时遇到的数据竞争问题。