定义:
Dispatch Semaphore是持有计数的信号,该计数是多线程编程中的计数类型信号。所谓信号,类似于过马路时常用的手旗。可以通过时举起手旗,不可通过时放下手旗。而在Dispatch Semaphore中,使用计数来实现该功能。
计数为0时等待,计数为1或大于1时,减去1而不等待。
信号量为0则阻塞线程,大于0则不会阻塞。则我们通过改变信号量的值,来控制是否阻塞线程,从而达到线程同步
其实,这有点类似锁机制了,只不过信号量都是系统帮助我们处理了,我们只需要在执行线程之前,设定一个信号量值,并且在使用时,加上信号量处理方法就行了。
函数说明
信号量相关的主要有3个函数,分别是:
// 创建信号量,参数:信号量的初值,如果小于0则会返回NULL
dispatch_semaphore_create(信号量初始值)
// 等待计数值达到大于或等于1,当计数值大于等于1,或者在待机中计数值大于等于1时,对该计数值进行减1,并从dispatch_semaphore_wait返回。
// 等待时间,可以指定等待时间,判断在这个时间过去后,拿到dispatch_semaphore_wait函数的返回值,进行相关的处理
dispatch_semaphore_wait(信号量,等待时间)
// 提高信号量
dispatch_semaphore_signal(信号量)
注意:正常的使用顺序是先降低,然后再提高,这两个函数通常成对使用。
使用场景
- 假设现在系统有两个空闲资源可以被利用,但同一时间却有三个线程要进行访问,这种情况下,该如何处理呢?(排他控制,控制同时访问资源(执行任务)的线程数)
- (void)testSemaphore{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
dispatch_queue_t queue = dispatch_queue_create("test.semaphore", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
for (NSInteger i = 0; i < 5; i++) {
NSLog(@"任务1执行中,i = >> %ld",i);
sleep(1);
}
NSLog(@"任务1执行完毕");
dispatch_semaphore_signal(semaphore);
});
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
for (NSInteger i = 0; i < 5; i++) {
NSLog(@"任务2执行中,i = >> %ld",i);
sleep(1);
}
NSLog(@"任务2执行完毕");
dispatch_semaphore_signal(semaphore);
});
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
for (NSInteger i = 0; i < 5; i++) {
NSLog(@"任务3执行中,i = >> %ld",i);
sleep(1);
}
NSLog(@"任务3执行完毕");
dispatch_semaphore_signal(semaphore);
});
}
控制台输出:
2019-10-12 13:45:24.366651+0800 tianran[16396:4376103] 任务1执行中,i = >> 0
2019-10-12 13:45:24.366762+0800 tianran[16396:4376100] 任务2执行中,i = >> 0
2019-10-12 13:45:25.371936+0800 tianran[16396:4376103] 任务1执行中,i = >> 1
2019-10-12 13:45:25.371936+0800 tianran[16396:4376100] 任务2执行中,i = >> 1
2019-10-12 13:45:26.376977+0800 tianran[16396:4376100] 任务2执行中,i = >> 2
2019-10-12 13:45:26.377385+0800 tianran[16396:4376103] 任务1执行中,i = >> 2
2019-10-12 13:45:27.378956+0800 tianran[16396:4376103] 任务1执行中,i = >> 3
2019-10-12 13:45:27.382488+0800 tianran[16396:4376100] 任务2执行中,i = >> 3
2019-10-12 13:45:28.384444+0800 tianran[16396:4376103] 任务1执行中,i = >> 4
2019-10-12 13:45:28.386425+0800 tianran[16396:4376100] 任务2执行中,i = >> 4
2019-10-12 13:45:29.388009+0800 tianran[16396:4376100] 任务2执行完毕
2019-10-12 13:45:29.388514+0800 tianran[16396:4376102] 任务3执行中,i = >> 0
2019-10-12 13:45:29.389877+0800 tianran[16396:4376103] 任务1执行完毕
2019-10-12 13:45:30.393968+0800 tianran[16396:4376102] 任务3执行中,i = >> 1
2019-10-12 13:45:31.399738+0800 tianran[16396:4376102] 任务3执行中,i = >> 2
2019-10-12 13:45:32.405557+0800 tianran[16396:4376102] 任务3执行中,i = >> 3
2019-10-12 13:45:33.411357+0800 tianran[16396:4376102] 任务3执行中,i = >> 4
2019-10-12 13:45:34.417180+0800 tianran[16396:4376102] 任务3执行完毕
由上面可以看到,同一时间内,只有两个任务或者两个线程在执行,任务1和任务2同时开始执行,当任务2执行完成之后,任务1还未执行完毕,任务3开始执行。
比如此时有一个空数组A,我们在三个线程中,对A数组进行操作,我们可以将信号量的初始值设置为1,来确保同一时间只有一个线程对A数组进行读取操作,也可以达到锁的功能。
- 比如要下载很多图片,并发异步执行,每个下载都会开辟一个新线程,可是开辟太多线程肯定cpu吃不消,所以可以用信号量控制一下最大开辟线程数:
//crate的value表示,最多几个资源可访问
dispatch_semaphore_t samphore = dispatch_semaphore_create(5);
dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i = 0; i < 15; i++) {
dispatch_async(quene, ^{
dispatch_semaphore_wait(samphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task %d", i);
[self downloadImge:^(BOOL isSuccess, NSString *errorMsg) {
NSLog(@"BLock completed %d", i);
dispatch_semaphore_signal(samphore);
}];
});
}
由于是异步执行的,所以每次循环block都会执行dispatch_semaphore_wait,从而 semaphore-1。当5次循环后semaphore==0,则会阻塞线程,直到执行了block的dispatch_semaphore_signal才会继续执行。
- 在开发中我们需要处理下载多张照片等待所有网络回调完之后才执行后面的操作。或者等待多个网络请求,所有请求成功数据融合处理。
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i = 0; i < 5; i++) {
dispatch_group_async(group, quene, ^{
dispatch_semaphore_t samphore = dispatch_semaphore_create(0);
[self downloadImge:^(BOOL isSuccess, NSString *errorMsg) {
NSLog(@"task %d completed ", i);
dispatch_semaphore_signal(samphore);
}];
dispatch_semaphore_wait(samphore, DISPATCH_TIME_FOREVER);
});
}
dispatch_group_notify(group, quene, ^{
NSLog(@"All task completed ====");
});
运行结果:
2017-12-20 14:22:03.162137+0800 OCDemo[64177:15790354] task 0 completed
2017-12-20 14:22:03.162138+0800 OCDemo[64177:15790351] task 3 completed
2017-12-20 14:22:04.165827+0800 OCDemo[64177:15790352] task 2 completed
2017-12-20 14:22:05.167003+0800 OCDemo[64177:15790368] task 4 completed
2017-12-20 14:22:05.166964+0800 OCDemo[64177:15790353] task 1 completed
2017-12-20 14:22:05.167153+0800 OCDemo[64177:15790368] All task completed ====
主要是用到了,当信号值为0的时候会阻塞当前线程这个功能。