Task之计数信号量

2019-07-10 15:31:26 浏览数 (2)

在<Task之二进制信号量>里提到过二进制信号量用来解决同步问题。下面看一个同步的例子

这段代码很简单,大致的意思就是每成功申请一次信号量,就打印一句话

启动一个任务(t1)来调用这个函数:

可以看到t1阻塞到信号量semId上了。直接给它释放一次信号量

任务(t1)打印了一句话,说明收到了一次信号量

接下来试试释放两次信号量,可以用Shell的命令repeat()

任务(t1)也打印了两句话,说明收到了两次信号量

接下来试试多次的

可以看到, repeat的次数大于2之后,任务(t1)都是只能收到两次信号量

我们看看semGive()的操作流程

从上图可以看到,repeat()第一次释放信号量时,它会将阻塞状态的t1置为就绪状态。第二次释放时,没有任务阻塞了,于是将信号量置为有效(0->1),之后再释放时,都是将信号量置有效(1->1)。直到repeat()执行完毕,就绪状态的t1开始执行后续操作,出现第一次打印。然后又可以成功申请一次信号量(1->0),就有了第二次打印。这之后,信号量就又是无效的了,t1再次进入了阻塞状态

这就是二进制信号量的特点,它是用来表示事件是否发生了,而不能表示事件发生的次数

如果需要记录事件发生的次数呢?可以试试提高t1的优先级

不过VxWorks专门提供了用于计数的信号量: 计数信号量

semCCreate()用来动态创建计数信号量,semCInit()用来初始化静态分配的信号量

initCount表示计数信号量的初值,因为是有符号整型值,其取值范围是0-2147483647(0x0-0x7fffffff)

而具体的使用,与二进制信号量非常像

semTake()用来申请信号量,信号量无效时,引起阻塞,因此不能在ISR中使用

semGive()用来释放信号量,在任务或ISR中都可以调用

与二进制信号量不同的是,计数信号量的值不是在0和1之间变化,而是用一个count来记录具体数值。而且目前count的值可以超过2147483647(0x7fffffff)

超过之后,semGive()和semTake()还可以正常操作。只是show()操作时,只能看到低31位的值

从源码里可以看到,只有count超过4294967295(0xffffffff)时,才会溢出。看来这应该是VxWorks的一个小bug了

Anyway,实际应用中,count的值不太可能那么大的。还是回到开始位置,把testSemB那个例子改了看看吧

这时候再多次释放信号量,任务(t1)就可以收到多次了

同时,计数信号量也支持semFlush()操作,即它也是可以用于多任务同步的。

最后跑一个静态初始化的例子吧

mySem是在编译时就分配空间了,semCInit()里就不用在动态申请内存了

这正是:

两种信号量,不分弱与强。

同步或计数,用时细端详。

0 人点赞