MST

星途 面试题库

面试题:Go并发环境下使用信号量控制资源竞争与动态资源分配

假设有一个系统,有若干个任务需要执行,每个任务需要占用一定数量的资源(例如文件描述符等),系统总共只有固定数量的此类资源。请利用Go语言的信号量机制(可自行实现或使用`golang.org/x/sync/semaphore`包)来控制任务对资源的获取与释放,确保资源竞争不会发生,同时要能动态分配资源给不同优先级的任务(可以简单定义任务优先级),请详细阐述设计思路并给出核心代码实现。
21.3万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 资源管理:使用信号量来表示系统中可用的资源数量。可以借助 golang.org/x/sync/semaphore 包,该包提供了一个简单的信号量实现。
  2. 任务优先级:为每个任务定义一个优先级字段。优先级可以用整数表示,数字越大优先级越高。
  3. 任务队列:使用一个优先级队列来存储等待资源的任务。优先级高的任务先获取资源。

核心代码实现

package main

import (
    "container/heap"
    "fmt"
    "golang.org/x/sync/semaphore"
    "sync"
)

// Task 定义任务结构
type Task struct {
    id       int
    priority int
    // 这里假设任务需要的资源数量固定为1,实际可按需调整
    resourceNeeded int
}

// PriorityQueue 定义优先级队列
type PriorityQueue []*Task

func (pq PriorityQueue) Len() int { return len(pq) }

func (pq PriorityQueue) Less(i, j int) bool {
    // 这里按照优先级从高到低排序
    return pq[i].priority > pq[j].priority
}

func (pq PriorityQueue) Swap(i, j int) {
    pq[i], pq[j] = pq[j], pq[i]
}

func (pq *PriorityQueue) Push(x interface{}) {
    *pq = append(*pq, x.(*Task))
}

func (pq *PriorityQueue) Pop() interface{} {
    old := *pq
    n := len(old)
    item := old[n - 1]
    *pq = old[0 : n - 1]
    return item
}

func main() {
    var wg sync.WaitGroup
    // 假设系统有10个资源
    sem := semaphore.NewWeighted(10)
    taskQueue := &PriorityQueue{}
    heap.Init(taskQueue)

    // 模拟添加任务
    tasks := []Task{
        {id: 1, priority: 3, resourceNeeded: 1},
        {id: 2, priority: 1, resourceNeeded: 1},
        {id: 3, priority: 2, resourceNeeded: 1},
    }
    for _, task := range tasks {
        heap.Push(taskQueue, &task)
    }

    for taskQueue.Len() > 0 {
        task := heap.Pop(taskQueue).(*Task)
        wg.Add(1)
        go func(t *Task) {
            defer wg.Done()
            // 获取资源
            if err := sem.Acquire(nil, t.resourceNeeded); err != nil {
                fmt.Printf("Task %d failed to acquire resource: %v\n", t.id, err)
                return
            }
            defer sem.Release(t.resourceNeeded)
            fmt.Printf("Task %d with priority %d is running\n", t.id, t.priority)
            // 模拟任务执行
        }(task)
    }
    wg.Wait()
}

在这段代码中:

  1. Task 结构体定义了任务的属性,包括任务ID、优先级和所需资源数量。
  2. PriorityQueue 实现了 heap.Interface 接口,用于实现优先级队列。
  3. main 函数中,初始化了信号量和任务队列,并将任务添加到队列中。
  4. 通过 go 协程并发执行任务,每个任务在执行前获取资源,执行完毕后释放资源,确保资源竞争不会发生。