4. 计数信号量管理资源池

任务定义

本案例在裸机环境下模拟一个包含3个相同资源的资源池,使用计数信号量控制3个并发任务对资源的申请与释放,确保任意时刻最多只有3个任务持有资源,第4个申请者必须等待。任务通过串口打印申请/释放事件,便于观测信号量计数变化。

计数信号量机制

计数信号量维护一个整型计数值count,取值范围为[0, maxCount]。核心操作:

  • 获取(take):若count > 0则减1并立即返回成功;否则返回失败(本案例采用非阻塞方式,由任务自行处理等待)。
  • 释放(give):若count < maxCount则加1;否则保持上限不变。

该机制天然防止超额分配:资源池有3个资源,信号量初值设为3,每次成功take代表占用1个资源,give代表归还1个资源。

代码实现

<code>// semaphore_pool.c
#include <stdio.h>
#include <stdint.h>
#include <string.h>

#define MAX_RESOURCES  3
#define NUM_TASKS      3
#define SIM_STEPS       6   // 每个任务执行的操作步数

typedef struct {
    int8_t count;
    int8_t maxCount;
} Semaphore;

// 信号量初始化
void sem_init(Semaphore *sem, int8_t maxCount) {
    sem->count = maxCount;
    sem->maxCount = maxCount;
}

// 非阻塞获取:成功返回1,失败返回0
int sem_take(Semaphore *sem) {
    if (sem->count > 0) {
        sem->count--;
        return 1;
    }
    return 0;
}

// 释放:不超过上限
void sem_give(Semaphore *sem) {
    if (sem->count < sem->maxCount) {
        sem->count++;
    }
}

// 模拟任务控制块
typedef struct {
    uint8_t id;
    uint8_t step;
    uint8_t holding;   // 当前是否持有资源
} Task;

Semaphore pool;   // 资源池信号量

// 执行单个任务的一步操作
void task_step(Task *t) {
    if (!t->holding) {
        // 尝试申请资源
        if (sem_take(&pool)) {
            t->holding = 1;
            printf("[Task %d] 获取资源成功, 剩余=%d\n", t->id, pool.count);
        } else {
            printf("[Task %d] 获取资源失败(池空), 剩余=%d\n", t->id, pool.count);
        }
    } else {
        // 释放资源
        sem_give(&pool);
        t->holding = 0;
        printf("[Task %d] 释放资源, 剩余=%d\n", t->id, pool.count);
    }
    t->step++;
}

int main(void) {
    Task tasks[NUM_TASKS];
    int i;

    sem_init(&pool, MAX_RESOURCES);
    printf("资源池初始化: 共%d个资源\n", MAX_RESOURCES);

    // 初始化任务:每个任务先申请后释放,交替执行
    for (i = 0; i < NUM_TASKS; i++) {
        tasks[i].id = i + 1;
        tasks[i].step = 0;
        tasks[i].holding = 0;
    }

    // 轮转调度模拟
    int total_steps = NUM_TASKS * SIM_STEPS;
    for (int s = 0; s < total_steps; s++) {
        Task *cur = &tasks[s % NUM_TASKS];
        task_step(cur);
    }

    // 最终校验:所有任务应已释放资源
    int all_released = 1;
    for (i = 0; i < NUM_TASKS; i++) {
        if (tasks[i].holding) {
            all_released = 0;
            break;
        }
    }
    printf("最终信号量计数=%d, 所有任务已释放=%s\n",
           pool.count, all_released ? "是" : "否");
    return 0;
}

编译与运行

gcc -o semaphore_pool semaphore_pool.c
./semaphore_pool

验证方法与判定标准

运行程序后观察串口输出,满足以下条件即判定通过:

  1. 信号量计数始终在0~3之间,从未出现负数或超过3;
  2. 当计数为0时,任务申请资源打印“获取资源失败(池空)”,不会出现超额分配;
  3. 释放后计数递增,后续任务可继续获取;
  4. 程序结束时打印“最终信号量计数=3”且“所有任务已释放=是”。

预期输出(节选)

资源池初始化: 共3个资源
[Task 1] 获取资源成功, 剩余=2
[Task 2] 获取资源成功, 剩余=1
[Task 3] 获取资源成功, 剩余=0
[Task 1] 获取资源失败(池空), 剩余=0
[Task 2] 获取资源失败(池空), 剩余=0
[Task 3] 获取资源失败(池空), 剩余=0
[Task 1] 释放资源, 剩余=1
[Task 2] 释放资源, 剩余=2
[Task 3] 释放资源, 剩余=3
...
最终信号量计数=3, 所有任务已释放=是
计数信号量控制资源池(3个资源) 资源池 资源 1 资源 2 资源 3 计数信号量 count = 3 控制 任务(3个) Task 1 Task 2 Task 3 可用资源 任务 信号量计数器 take: count-- give: count++