MST

星途 面试题库

面试题:Go闭包在并发场景下的应用与问题

在Go语言的并发编程中,闭包的使用非常广泛。请描述一个在并发场景下使用闭包的实际案例,并分析可能会出现的问题及如何解决这些问题,例如资源竞争、数据一致性等。
28.7万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

并发场景下使用闭包的实际案例

假设有一个任务分发系统,需要将一批任务分发给多个goroutine并行处理,每个goroutine处理完任务后要更新共享的结果数据。

package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup
    results := make(map[int]int)
    mutex := &sync.Mutex{}

    for i := 0; i < 10; i++ {
        wg.Add(1)
        // 使用闭包将任务逻辑封装起来
        go func(taskID int) {
            defer wg.Done()
            // 模拟任务处理
            result := taskID * taskID
            mutex.Lock()
            results[taskID] = result
            mutex.Unlock()
        }(i)
    }

    wg.Wait()
    fmt.Println(results)
}

可能出现的问题

  1. 资源竞争:多个goroutine同时访问和修改 results 这个共享数据结构,可能会导致数据不一致。例如,一个goroutine正在读取 results 的值,而另一个goroutine同时在修改它。

解决方法

  1. 使用互斥锁(Mutex):如上述代码中,通过 sync.Mutex 来保护对共享数据 results 的访问。在读取或写入 results 之前,调用 mutex.Lock() 锁定资源,操作完成后调用 mutex.Unlock() 释放锁。这样可以确保同一时间只有一个goroutine能够访问和修改 results,从而避免资源竞争和保证数据一致性。
  2. 读写锁(RWMutex):如果读操作远多于写操作,可以使用 sync.RWMutex。读操作时使用 RLock()RUnlock(),写操作时使用 Lock()Unlock()。读操作可以并发进行,而写操作会独占资源,从而在保证数据一致性的同时提高性能。
package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup
    results := make(map[int]int)
    rwMutex := &sync.RWMutex{}

    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(taskID int) {
            defer wg.Done()
            result := taskID * taskID
            rwMutex.Lock()
            results[taskID] = result
            rwMutex.Unlock()
        }(i)
    }

    // 模拟读操作
    go func() {
        rwMutex.RLock()
        fmt.Println(results)
        rwMutex.RUnlock()
    }()

    wg.Wait()
}

这种方式在读多写少的场景下能有效提升性能,同时保证数据一致性。