信号量有没有容量限制?

之前一直误以为信号量初始化的时候那个初始化的值是信号量的“容量”,昨天同事指出了我的错误,最初我是不相信的,经过以下代码实践,证明了我的错误:

Java版:

import java.util.concurrent.Semaphore;

public class Driver {

    private Semaphore semaphore;

    public Driver(int num){
        semaphore = new Semaphore(num);
    }

    /**
     * [driveCar description]
     * 假设:我们假定信号量初始化的时候,构造函数中的值是信号量的 容量
     * 验证:如果设置容量为 1 在释放第一次之后 第二次释放将是非法操作 一定会报相关错误
     */

    public void driveCar() {
        try {
            semaphore.acquire();//获取信号量
            int permits = semaphore.availablePermits();//当前可用的信号量
            System.out.println("1permits is "+permits);
            semaphore.release();//释放信号量
            permits = semaphore.availablePermits();
            System.out.println("2permits is "+permits);
            semaphore.release();//再次释放信号量
            permits = semaphore.availablePermits();
            System.out.println("3permits is "+permits);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

编写测试类如下:

class Test {
	public static void main(String[] args) {
		int num = Integer.valueOf(args[0]);
		Driver driver = new Driver(num);
		driver.driveCar();
	}
}

执行测试:java test 1得到结果如下

1permits is 0
2permits is 1
3permits is 2

由此说明,每次信号量释放的时候给信号量的值加1,但是其初始化的值,并不影响这个加1的操作,在 Java 中这个容量固定的假设不成立。

于是我还是不死心,继续去验证了 C 语言版本的,代码如下:

#include <semaphore.h>
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
sem_t binSem;
int sval = 0;

int main(int argc,char* argv[])
{
    int res = 0;
    int num = (int)(*argv[1]) - 48;
    printf("num is %d\n",num);
    res = sem_init(&binSem,1,num);
    
    if (res) {
         printf("Semaphore initialization failed!!\n");
         //exit(EXIT_FAILURE);
     }

    sem_wait(&binSem);
    sem_getvalue(&binSem, &sval);
    printf("1permits %d\n",sval);

    sem_post(&binSem);
    sem_getvalue(&binSem, &sval);
    printf("2permits %d\n",sval);

    sem_post(&binSem);
    sem_getvalue(&binSem, &sval);
    printf("3permits %d\n",sval);
}

编译的时候一定要加 -pthread 选项,不然编译会报类似如下错误:


/tmp/cc2tarEg.o: In function `main':
sem.c:(.text+0x51): undefined reference to `sem_init'
sem.c:(.text+0x6e): undefined reference to `sem_wait'
sem.c:(.text+0x7d): undefined reference to `sem_getvalue'
sem.c:(.text+0x9e): undefined reference to `sem_post'
sem.c:(.text+0xad): undefined reference to `sem_getvalue'
sem.c:(.text+0xce): undefined reference to `sem_post'
sem.c:(.text+0xdd): undefined reference to `sem_getvalue'

这里我是这么编译的:gcc sem.c -pthread -o sem

然后执行 ./sem 1

输出:

num is 1
1permits 0
2permits 1
3permits 2

彻底纠正了我一直以来的认知错误。

转载于:https://my.oschina.net/zwyang/blog/1861022

<think>嗯,用户想深入了解FreeRTOS中计数信号量和二进制信号量的区别及选择依据。从用户补充的引用来看,ta已经查阅过相关资料,但需要更系统的对比分析。用户上次提问关注计数信号量的使用场景,这次显然是想延伸理解两者的差异。 用户可能正在设计一个多任务系统,需要选择合适的同步机制。ta提到“选择依据”,说明需要实操指导而非纯理论。引用[2][5]提到优先级继承和互斥锁的区别,这点需要重点展开,因为这是二进制信号量与计数信号量最容易被混淆的特性。 从技术角度看,核心差异在于: 1. 容量差异:二进制信号量只有0/1两种状态(类似开关),而计数信号量允许0-N的计数值(类似资源计数器)。引用[3]的xSemaphoreCreateCounting()参数充分体现了这点。 2. 行为差异:二进制信号量在Give时若已满会失败(引用[4]的pdFAIL),而计数信号量在未达maxCount时可累积。 3. 优先级处理:二进制信号量无优先级继承,计数信号量同样没有(引用[2]明确对比了互斥锁的优先级继承机制)。这点特别重要,因为用户若需要防止优先级反转,应该用互斥量而非信号量。 使用场景的选择关键看“资源是否可叠加”: - 二进制信号量适合单次事件通知(如中断触发),或单个资源的互斥访问。引用[1]的同步示例很典型。 - 计数信号量适合资源池管理(如缓冲区块)或事件计数(如多个传感器触发),这点在用户上次提问时已详细说明。 需要提醒用户注意常见误区:有人误用二进制信号量管理多个资源(如连续Give两次模拟计数),这会导致资源竞争。引用[2]末尾强调“使用任务通知更高效”,这也是替代方案。 最后考虑用表格对比更直观,但需注意移动端显示效果。示例代码选二进制信号量的中断同步场景,因为其最体现二进制信号量的不可累积特性。</think>### FreeRTOS中计数信号量与二进制信号量的区别及选择依据 #### **核心区别对比** | **特性** | **二进制信号量** | **计数信号量** | |------------------------|----------------------------------|------------------------------------| | **状态范围** | 仅0或1(类似开关) | 0~N(N为创建时设定的最大值) | | **资源管理能力** | 管理单个资源 | 管理多个相同资源(资源池) | | **Give操作限制** | 若值为1时再次Give会失败(pdFAIL)[^4] | 可连续Give直到达到最大值 | | **典型应用场景** | 任务同步、中断与任务通信 | 资源池管理、事件计数、生产者-消费者 | | **优先级继承** | 不支持(与互斥量关键区别)[^2] | 不支持 | --- #### **详细差异分析** 1. **行为机制差异** - **二进制信号量** 行为类似“锁存器”: ```c xSemaphore = xSemaphoreCreateBinary(); // 初始值为0(空状态) xSemaphoreGive(xSemaphore); // 值变为1(释放) xSemaphoreTake(xSemaphore); // 值变回0(获取) ``` - **关键限制**:连续两次`Give()`会失败(值保持为1)[^4],适合单次事件通知。 - **计数信号量** 行为类似“计数器”: ```c // 创建最大值为5,初始值为3的信号量 xSemaphore = xSemaphoreCreateCounting(5, 3); xSemaphoreGive(xSemaphore); // 值+1(若未达5) xSemaphoreTake(xSemaphore); // 值-1(若>0) ``` - 可累积事件(如记录中断触发次数)[^3]。 2. **使用场景对比** - **二进制信号量适用场景** - 中断服务程序(ISR)向任务发送单次事件通知 ```c // ISR中释放信号量 void vSensorISR() { BaseType_t xHigherPriorityTaskWoken = pdFALSE; xSemaphoreGiveFromISR(xBinarySem, &xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } ``` - 两个任务间的简单同步(如任务A完成后触发任务B)[^1]。 - **计数信号量适用场景** - 管理资源池(如3个UART端口): ```c // 任务获取UART端口 if (xSemaphoreTake(xCountingSem, portMAX_DELAY)) { use_uart_port(); // 使用资源 xSemaphoreGive(xCountingSem); // 释放资源 } ``` - 生产者-消费者模型中跟踪缓冲区数据量[^3]。 3. **与互斥量的关键区别** - **优先级继承**:二进制/计数信号量**均不支持**优先级继承,而互斥量支持(防止优先级反转)[^5]。 - 例:高优先级任务等待信号量时,低优先级任务持有信号量不会被提升优先级,可能导致阻塞。 --- #### **选择依据总结** | **场景** | **推荐机制** | **原因** | |------------------------------|--------------------|----------------------------------------| | 单次事件通知(如ISR到任务) | 二进制信号量 | 轻量级,无计数需求 | | 管理多个相同资源(如内存块) | 计数信号量 | 精确控制资源数量 | | 共享资源的互斥访问 | **互斥量**(非信号量)| 需优先级继承防止优先级反转[^5] | | 高频事件记录(如传感器触发) | 计数信号量 | 可累积事件次数 | > 💡 **经验法则**: > - 需要**累计事件次数**或**管理多个资源** → 计数信号量 > - 仅需**单次事件触发** → 二进制信号量 > - 需**避免优先级反转** → 互斥量(而非信号量) --- #### **典型错误用法示例** ```c // 错误:试图用二进制信号量管理多个资源 xBinarySem = xSemaphoreCreateBinary(); xSemaphoreGive(xBinarySem); // 值=1(正常) xSemaphoreGive(xBinarySem); // 失败!值仍为1(pdFAIL)[^4] // 正确:用计数信号量管理 xCountingSem = xSemaphoreCreateCounting(5, 0); xSemaphoreGive(xCountingSem); // 值=1 xSemaphoreGive(xCountingSem); // 值=2(成功) ``` --- ### 总结 二进制信号量是**事件触发器**,计数信号量是**资源计数器**。选择取决于是否需要管理多个资源实例或累积事件: - 二进制信号量:轻量级同步,适合中断通知和简单任务同步[^1][^2]。 - 计数信号量:资源池管理、生产者-消费者模型和高频事件记录[^3]。 若需严格资源互斥且避免优先级反转,应使用互斥量而非信号量[^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值