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
验证方法与判定标准
运行程序后观察串口输出,满足以下条件即判定通过:
- 信号量计数始终在0~3之间,从未出现负数或超过3;
- 当计数为0时,任务申请资源打印“获取资源失败(池空)”,不会出现超额分配;
- 释放后计数递增,后续任务可继续获取;
- 程序结束时打印“最终信号量计数=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, 所有任务已释放=是