面试题答案
一键面试设计架构
- 请求处理函数工厂:
- 首先,创建一个函数工厂,用于生成针对每个请求的具体处理函数。这个工厂函数接受与请求相关的初始参数,例如请求的上下文、配置等。
func requestHandlerFactory(ctx context.Context, config RequestConfig) func() error { // 这里的状态变量会被闭包捕获 var sharedState SomeSharedState // 初始化共享状态 sharedState.init() return func() error { // 处理请求的具体逻辑 err := step1(ctx, sharedState) if err!= nil { return err } err = step2(ctx, sharedState) if err!= nil { return err } // 依此类推,执行其他步骤 return nil } }
- 并发处理:
- 使用Go的goroutine来并发处理请求。将生成的处理函数放入goroutine中执行。
func handleRequests(requests []Request) { var wg sync.WaitGroup for _, req := range requests { ctx := context.Background() config := getRequestConfig(req) handler := requestHandlerFactory(ctx, config) wg.Add(1) go func() { defer wg.Done() err := handler() if err!= nil { // 处理错误 log.Println("Request handling error:", err) } }() } wg.Wait() }
闭包管理状态
- 状态捕获:闭包捕获了在工厂函数中定义的状态变量(如
sharedState
)。每个请求处理函数实例都有自己独立的状态副本(假设状态的初始化是独立的),避免了不同请求之间状态的直接干扰。 - 状态更新:在闭包内部的处理逻辑中,状态的更新是按照操作序列顺序进行的。由于每个闭包实例独立处理请求,不会出现不同请求对同一状态的无序更新。
处理并发
- goroutine调度:通过将每个请求处理函数放入goroutine中,Go的运行时系统会自动调度这些goroutine,利用多核CPU的优势,提高整体的处理效率。
- 上下文控制:使用
context.Context
来管理请求的生命周期。例如,在父上下文取消时,所有相关的请求处理函数可以及时退出,避免资源浪费。
解决潜在资源竞争问题
- 独立状态副本:由于每个闭包实例有自己独立的状态副本,不存在多个goroutine同时访问和修改同一状态变量的问题,从而避免了大部分资源竞争。
- 同步机制(若有必要):如果存在真正需要共享的资源(如数据库连接池等),可以使用
sync.Mutex
或sync.RWMutex
等同步工具来保护共享资源的访问。例如:
这样可以确保在高并发情况下,对共享资源的访问是线程安全的。var dbMutex sync.Mutex func step1(ctx context.Context, sharedState SomeSharedState) error { dbMutex.Lock() defer dbMutex.Unlock() // 访问数据库的逻辑 return nil }