信号量的作用是用来控制资源/任务的并发量。这个怎么理解呢?
举个栗子:
体育课期中考试需要考核同一年级某个班的同学的体能是否达标,学校决定采用跑步并计算所用平均时间的方式,为了增加趣味及加强同学之间的凝聚力,体育老师们一致决定采用接力跑的方式,每个班级有3个接力棒,绕着足球场跑完一圈才可以交接棒,直到所有同学跑完为止。
那么我们可以想象一下是这样的一个场景:某班有47位同学,一开始因为只有三个接力棒,所以只能有三个同学站在起跑线,等发号枪响,三个同学就开始起跑,这里面有同学反应快会起跑得好一些,有同学经常锻炼可能会更快跑完一圈然后交棒,也就是过程的多样性问题,也比较贴合我们的编程的场景。
但是,在接力棒回来之前,剩下的44位同学只能在起跑点原地等待,直到正在进行跑步的同学回来并交接下一位同学才能起跑,按照这样的规则直到47位同学跑完即考核结束。
后话:计算47位同学跑一圈所用的平均时间即为某班的体能考核结果并全年级排名!
先看api:
dispatch_semaphore_create(long value) --- ①
dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout) --- ②
dispatch_semaphore_signal(dispatch_semaphore_t dsema) --- ③
这里对上面的api以及结合之前的栗子做一个说明:
①
所传参数 value
理解为一个班级的一次考试有多少个接力棒,也可以理解为我们编程时能够分配的资源的数量
②
所传参数dsema
也就是上面创建的信号量,timeout
理解为等待前面运行的任务执行多长时间为止,上面跑步的例子是,现实场景是要一直等前面的同学回来才能继续跑,而不是比如等10s
没有同学回来就可以继续跑,没有交接棒我们是不能跑的,那么我们这里的取值应该是:DISPATCH_TIME_FOREVER
,现实意义就是拿走一根接力棒,能够继续拿接力棒跑的名额少掉一个,当没有交接棒是空闲时所有同学都要等待前面至少一位同学跑完。
③
所传参数dsema
同理 也就是上面创建的信号量,这里的意义就是跑完一位同学,释放
出一个接力棒,其他同学就有了接力棒可以拿着接力棒继续跑。
需要注意的是:信号总数是应该先减后加
的,也就是先dispatch_semaphore_wait
,后dispatch_semaphore_signal
demo:
dispatch_semaphore_t semaphore = dispatch_semaphore_create(3);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i = 1; i <= 47; i++)
{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"学号为%d的同学开始跑",i);
dispatch_async(queue, ^{
int time = arc4random() % 5;
sleep(time);
NSLog(@"学号为%d同学跑完,用时:%d秒",i ,time);
dispatch_semaphore_signal(semaphore);
});
}
留意一下控制台里面输出的内容可以发现:同一时刻最多有三位同学在跑步,也就是有三个任务在执行,跑完一位同学才能有另一位同学继续跑,直到47位同学都跑完位置。
output:
20xx-xx-xx 17:09:50.727 CZB[74203:9934779] 学号为1的同学开始跑
20xx-xx-xx 17:09:50.727 CZB[74203:9934779] 学号为2的同学开始跑
20xx-xx-xx 17:09:50.727 CZB[74203:9934779] 学号为3的同学开始跑
20xx-xx-xx 17:09:51.731 CZB[74203:9934870] 学号为3同学跑完,用时:1秒
20xx-xx-xx 17:09:51.732 CZB[74203:9934779] 学号为4的同学开始跑
20xx-xx-xx 17:09:53.731 CZB[74203:9934869] 学号为2同学跑完,用时:3秒
20xx-xx-xx 17:09:53.731 CZB[74203:9934779] 学号为5的同学开始跑
20xx-xx-xx 17:09:53.734 CZB[74203:9934870] 学号为4同学跑完,用时:2秒
20xx-xx-xx 17:09:53.734 CZB[74203:9934779] 学号为6的同学开始跑
20xx-xx-xx 17:09:53.735 CZB[74203:9934870] 学号为6同学跑完,用时:0秒
20xx-xx-xx 17:09:53.735 CZB[74203:9934779] 学号为7的同学开始跑
20xx-xx-xx 17:09:54.731 CZB[74203:9934861] 学号为1同学跑完,用时:4秒
20xx-xx-xx 17:09:54.732 CZB[74203:9934779] 学号为8的同学开始跑
20xx-xx-xx 17:09:55.740 CZB[74203:9934870] 学号为7同学跑完,用时:2秒
20xx-xx-xx 17:09:55.740 CZB[74203:9934779] 学号为9的同学开始跑
20xx-xx-xx 17:09:57.736 CZB[74203:9934869] 学号为5同学跑完,用时:4秒
20xx-xx-xx 17:09:57.736 CZB[74203:9934779] 学号为10的同学开始跑
20xx-xx-xx 17:09:57.738 CZB[74203:9934861] 学号为8同学跑完,用时:3秒
20xx-xx-xx 17:09:57.739 CZB[74203:9934779] 学号为11的同学开始跑
20xx-xx-xx 17:09:57.739 CZB[74203:9934861] 学号为11同学跑完,用时:0秒
20xx-xx-xx 17:09:57.740 CZB[74203:9934779] 学号为12的同学开始跑
20xx-xx-xx 17:09:58.743 CZB[74203:9934861] 学号为12同学跑完,用时:1秒
20xx-xx-xx 17:09:58.743 CZB[74203:9934870] 学号为9同学跑完,用时:3秒
20xx-xx-xx 17:09:58.743 CZB[74203:9934779] 学号为13的同学开始跑
20xx-xx-xx 17:09:58.744 CZB[74203:9934779] 学号为14的同学开始跑
20xx-xx-xx 17:09:58.744 CZB[74203:9934870] 学号为13同学跑完,用时:0秒
20xx-xx-xx 17:09:58.744 CZB[74203:9934779] 学号为15的同学开始跑
20xx-xx-xx 17:10:00.748 CZB[74203:9934870] 学号为15同学跑完,用时:2秒
20xx-xx-xx 17:10:00.749 CZB[74203:9934779] 学号为16的同学开始跑
20xx-xx-xx 17:10:01.741 CZB[74203:9934869] 学号为10同学跑完,用时:4秒
20xx-xx-xx 17:10:01.742 CZB[74203:9934779] 学号为17的同学开始跑
20xx-xx-xx 17:10:01.747 CZB[74203:9934861] 学号为14同学跑完,用时:3秒
20xx-xx-xx 17:10:01.748 CZB[74203:9934779] 学号为18的同学开始跑
20xx-xx-xx 17:10:01.748 CZB[74203:9934861] 学号为18同学跑完,用时:0秒
20xx-xx-xx 17:10:01.748 CZB[74203:9934779] 学号为19的同学开始跑
20xx-xx-xx 17:10:04.743 CZB[74203:9934869] 学号为17同学跑完,用时:3秒
20xx-xx-xx 17:10:04.743 CZB[74203:9934779] 学号为20的同学开始跑
20xx-xx-xx 17:10:04.753 CZB[74203:9934870] 学号为16同学跑完,用时:4秒
20xx-xx-xx 17:10:04.754 CZB[74203:9934779] 学号为21的同学开始跑
20xx-xx-xx 17:10:05.752 CZB[74203:9934861] 学号为19同学跑完,用时:4秒
20xx-xx-xx 17:10:05.752 CZB[74203:9934779] 学号为22的同学开始跑
20xx-xx-xx 17:10:07.744 CZB[74203:9934869] 学号为20同学跑完,用时:3秒
20xx-xx-xx 17:10:07.744 CZB[74203:9934779] 学号为23的同学开始跑
20xx-xx-xx 17:10:07.755 CZB[74203:9934861] 学号为22同学跑完,用时:2秒
20xx-xx-xx 17:10:07.755 CZB[74203:9934870] 学号为21同学跑完,用时:3秒
20xx-xx-xx 17:10:07.756 CZB[74203:9934779] 学号为24的同学开始跑
20xx-xx-xx 17:10:07.756 CZB[74203:9934779] 学号为25的同学开始跑
20xx-xx-xx 17:10:09.756 CZB[74203:9934861] 学号为25同学跑完,用时:2秒
20xx-xx-xx 17:10:09.757 CZB[74203:9934779] 学号为26的同学开始跑
20xx-xx-xx 17:10:09.757 CZB[74203:9934861] 学号为26同学跑完,用时:0秒
20xx-xx-xx 17:10:09.757 CZB[74203:9934779] 学号为27的同学开始跑
20xx-xx-xx 17:10:09.758 CZB[74203:9934861] 学号为27同学跑完,用时:0秒
20xx-xx-xx 17:10:09.758 CZB[74203:9934779] 学号为28的同学开始跑
20xx-xx-xx 17:10:10.748 CZB[74203:9934869] 学号为23同学跑完,用时:3秒
20xx-xx-xx 17:10:10.748 CZB[74203:9934779] 学号为29的同学开始跑
20xx-xx-xx 17:10:11.751 CZB[74203:9934869] 学号为29同学跑完,用时:1秒
20xx-xx-xx 17:10:11.751 CZB[74203:9934779] 学号为30的同学开始跑
20xx-xx-xx 17:10:11.752 CZB[74203:9934869] 学号为30同学跑完,用时:0秒
20xx-xx-xx 17:10:11.752 CZB[74203:9934779] 学号为31的同学开始跑
20xx-xx-xx 17:10:11.761 CZB[74203:9934870] 学号为24同学跑完,用时:4秒
20xx-xx-xx 17:10:11.761 CZB[74203:9934779] 学号为32的同学开始跑
20xx-xx-xx 17:10:12.761 CZB[74203:9934861] 学号为28同学跑完,用时:3秒
20xx-xx-xx 17:10:12.761 CZB[74203:9934779] 学号为33的同学开始跑
20xx-xx-xx 17:10:12.764 CZB[74203:9934870] 学号为32同学跑完,用时:1秒
20xx-xx-xx 17:10:12.764 CZB[74203:9934779] 学号为34的同学开始跑
20xx-xx-xx 17:10:12.765 CZB[74203:9934870] 学号为34同学跑完,用时:0秒
20xx-xx-xx 17:10:12.765 CZB[74203:9934779] 学号为35的同学开始跑
20xx-xx-xx 17:10:15.755 CZB[74203:9934869] 学号为31同学跑完,用时:4秒
20xx-xx-xx 17:10:15.755 CZB[74203:9934779] 学号为36的同学开始跑
20xx-xx-xx 17:10:15.755 CZB[74203:9934869] 学号为36同学跑完,用时:0秒
20xx-xx-xx 17:10:15.756 CZB[74203:9934779] 学号为37的同学开始跑
20xx-xx-xx 17:10:15.766 CZB[74203:9934870] 学号为35同学跑完,用时:3秒
20xx-xx-xx 17:10:15.767 CZB[74203:9934779] 学号为38的同学开始跑
20xx-xx-xx 17:10:16.765 CZB[74203:9934861] 学号为33同学跑完,用时:4秒
20xx-xx-xx 17:10:16.765 CZB[74203:9934779] 学号为39的同学开始跑
20xx-xx-xx 17:10:17.760 CZB[74203:9934869] 学号为37同学跑完,用时:2秒
20xx-xx-xx 17:10:17.760 CZB[74203:9934779] 学号为40的同学开始跑
20xx-xx-xx 17:10:17.760 CZB[74203:9934869] 学号为40同学跑完,用时:0秒
20xx-xx-xx 17:10:17.761 CZB[74203:9934779] 学号为41的同学开始跑
20xx-xx-xx 17:10:17.767 CZB[74203:9934861] 学号为39同学跑完,用时:1秒
20xx-xx-xx 17:10:17.768 CZB[74203:9934779] 学号为42的同学开始跑
20xx-xx-xx 17:10:17.768 CZB[74203:9934861] 学号为42同学跑完,用时:0秒
20xx-xx-xx 17:10:17.768 CZB[74203:9934779] 学号为43的同学开始跑
20xx-xx-xx 17:10:18.774 CZB[74203:9934861] 学号为43同学跑完,用时:1秒
20xx-xx-xx 17:10:18.775 CZB[74203:9934779] 学号为44的同学开始跑
20xx-xx-xx 17:10:19.772 CZB[74203:9934870] 学号为38同学跑完,用时:4秒
20xx-xx-xx 17:10:19.772 CZB[74203:9934779] 学号为45的同学开始跑
20xx-xx-xx 17:10:19.772 CZB[74203:9934870] 学号为45同学跑完,用时:0秒
20xx-xx-xx 17:10:19.773 CZB[74203:9934779] 学号为46的同学开始跑
20xx-xx-xx 17:10:21.764 CZB[74203:9934869] 学号为41同学跑完,用时:4秒
20xx-xx-xx 17:10:21.764 CZB[74203:9934779] 学号为47的同学开始跑
20xx-xx-xx 17:10:22.773 CZB[74203:9934870] 学号为46同学跑完,用时:3秒
20xx-xx-xx 17:10:22.779 CZB[74203:9934861] 学号为44同学跑完,用时:4秒
20xx-xx-xx 17:10:23.769 CZB[74203:9934869] 学号为47同学跑完,用时:2秒
还有一个情况,如果想要提前结束考核,也就是结束任务那怎么办,我们回过头来再看一下dispatch_semaphore_create(long value)
和dispatch_semaphore_wait ()
。每执行一次dispatch_semaphore_wait ()
对应的信号总量的值: value
值就会减一,当信用总量的值小于0
时,将会结束所有的任务。
我们改一下代码:
dispatch_semaphore_t semaphore = dispatch_semaphore_create(3);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i = 1; i <= 47; i++)
{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
if (i == 7) {
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}
NSLog(@"学号为%d的同学开始跑",i);
dispatch_async(queue, ^{
int time = arc4random() % 5;
sleep(time);
NSLog(@"学号为%d同学跑完,用时:%d秒",i ,time);
dispatch_semaphore_signal(semaphore);
});
}
当 i
== 7时,我们调用 dispatch_semaphore_wait ()
4次,每调用一次信号量减1,由于我们总共只有3个信号量,所以会导致任务执行结束。
控制台输出结果是:
2017-10-31 17:38:01.228 CZB[74583:10003955] 学号为1的同学开始跑
2017-10-31 17:38:01.228 CZB[74583:10003955] 学号为2的同学开始跑
2017-10-31 17:38:01.228 CZB[74583:10003955] 学号为3的同学开始跑
2017-10-31 17:38:02.228 CZB[74583:10004053] 学号为1同学跑完,用时:1秒
2017-10-31 17:38:02.229 CZB[74583:10003955] 学号为4的同学开始跑
2017-10-31 17:38:02.230 CZB[74583:10004053] 学号为4同学跑完,用时:0秒
2017-10-31 17:38:02.230 CZB[74583:10003955] 学号为5的同学开始跑
2017-10-31 17:38:03.231 CZB[74583:10004053] 学号为5同学跑完,用时:1秒
2017-10-31 17:38:03.231 CZB[74583:10004045] 学号为3同学跑完,用时:2秒
2017-10-31 17:38:03.232 CZB[74583:10003955] 学号为6的同学开始跑
2017-10-31 17:38:03.232 CZB[74583:10004045] 学号为6同学跑完,用时:0秒
2017-10-31 17:38:04.228 CZB[74583:10004052] 学号为2同学跑完,用时:3秒
因此,如果想要提前结束任务可以根据具体情况执行 dispatch_semaphore_wait ()
来中断任务队列。