MST

星途 面试题库

面试题:Go递归函数与并发结合及优化

在Go语言中,给定一个任务列表,每个任务需要递归处理一些数据,并且这些任务之间可以并发执行。请设计并实现一个方案,使用递归函数和Go的并发特性高效完成这些任务,同时说明如何处理可能出现的资源竞争和死锁问题。
44.7万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

方案设计与实现

package main

import (
    "fmt"
    "sync"
)

// 定义任务结构体
type Task struct {
    Data []int
}

// 递归处理任务的函数
func processTask(task Task, wg *sync.WaitGroup, mutex *sync.Mutex) {
    defer wg.Done()

    // 处理任务数据
    for _, num := range task.Data {
        // 这里模拟实际的数据处理操作
        mutex.Lock()
        fmt.Printf("Processing number: %d\n", num)
        mutex.Unlock()
    }

    // 模拟递归任务
    if len(task.Data) > 1 {
        mid := len(task.Data) / 2
        subTask1 := Task{Data: task.Data[:mid]}
        subTask2 := Task{Data: task.Data[mid:]}

        var subWg sync.WaitGroup
        subWg.Add(2)

        go processTask(subTask1, &subWg, mutex)
        go processTask(subTask2, &subWg, mutex)

        subWg.Wait()
    }
}

func main() {
    tasks := []Task{
        {Data: []int{1, 2, 3, 4}},
        {Data: []int{5, 6, 7, 8}},
    }

    var wg sync.WaitGroup
    wg.Add(len(tasks))

    var mutex sync.Mutex

    for _, task := range tasks {
        go processTask(task, &wg, &mutex)
    }

    wg.Wait()
}

资源竞争处理

  1. 使用互斥锁(Mutex):在共享资源(如打印操作)访问处使用 sync.Mutex。在上述代码中,在打印数据时,通过 mutex.Lock()mutex.Unlock() 来确保同一时间只有一个协程能访问打印操作,防止资源竞争。
  2. 读写锁(RWMutex):如果存在大量读操作和少量写操作的场景,可以使用 sync.RWMutex。读操作时可以允许多个协程同时进行,写操作时则独占资源,以确保数据一致性。

死锁处理

  1. 合理的锁顺序:确保所有协程以相同的顺序获取锁。如果在代码中有多个锁,按照固定的顺序获取锁可以避免死锁。例如,如果有锁A和锁B,所有协程都先获取锁A,再获取锁B。
  2. 避免嵌套锁:尽量减少锁的嵌套使用。嵌套锁容易导致死锁,尤其是在复杂的逻辑中。如果必须使用嵌套锁,要仔细规划锁的获取和释放顺序。
  3. 使用超时机制:在获取锁时设置超时。Go语言的 context 包可以用于实现超时机制。如果在规定时间内没有获取到锁,可以放弃操作并进行相应处理,避免无限等待。例如:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
select {
case <-ctx.Done():
    // 处理获取锁超时的情况
    fmt.Println("Timeout while acquiring lock")
default:
    // 获取锁的操作
    mutex.Lock()
    defer mutex.Unlock()
    // 执行操作
}