面试题答案
一键面试设计思路
- 数据结构定义:定义一个结构体来表示要共享的数据,这里以一个简单的计数器结构体为例,包含一个计数值字段。
- Goroutine 职责:
- 多个
incrementer
Goroutine 负责增加计数器的值。 - 一个
printer
Goroutine 负责定期打印计数器的值。
- 多个
- Channel 使用:
- 使用一个无缓冲 channel 来传递对共享数据的操作请求。这样可以保证操作的顺序性,避免数据竞争。
- 操作请求可以是一个函数类型,在接收端执行这些函数,从而安全地修改共享数据。
代码实现
package main
import (
"fmt"
"sync"
"time"
)
// Counter 定义计数器结构体
type Counter struct {
value int
}
// Operation 定义对 Counter 的操作类型
type Operation func(*Counter)
// increment 增加计数器的值
func increment(c *Counter) {
c.value++
}
// print 打印计数器的值
func print(c *Counter) {
fmt.Printf("Current value: %d\n", c.value)
}
func main() {
counter := Counter{}
operationChan := make(chan Operation)
var wg sync.WaitGroup
// 启动多个 incrementer Goroutine
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for {
operationChan <- increment
time.Sleep(100 * time.Millisecond)
}
}()
}
// 启动 printer Goroutine
wg.Add(1)
go func() {
defer wg.Done()
for {
operationChan <- print
time.Sleep(500 * time.Millisecond)
}
}()
// 模拟运行一段时间后退出
go func() {
time.Sleep(2 * time.Second)
close(operationChan)
}()
wg.Wait()
}
上述代码通过 operationChan
这个 channel 来传递对 Counter
的操作,无论是增加计数值还是打印计数值,都通过这个 channel 顺序执行,从而避免了数据竞争。incrementer
Goroutine 定期向 channel 发送增加操作,printer
Goroutine 定期向 channel 发送打印操作,主函数在运行一段时间后关闭 channel,所有 Goroutine 结束运行。