GCD 信号量的理解与使用
引言:
信号量:就是一种可用来控制访问资源的数量的标识,设定了一个信号量,在线程访问之前,加上信号量的处理,则可告知系统按照我们指定的信号量数量来执行多个线程。
其实,这有点类似锁机制了,只不过信号量都是系统帮助我们处理了,我们只需要在执行线程之前,设定一个信号量值,并且在使用时,加上信号量处理方法就行了。
# 一. 三个函数
# 1. dispatch_semaphore_create
dispatch_semaphore_create(信号量值)
创建信号量
传入的参数为long,输出一个dispatch_semaphore_t
类型且值为value的信号量。
值得注意的是,这里的传入的参数value必须大于或等于0,否则dispatch_semaphore_create会返回NULL。
# 2. dispatch_semaphore_signal
dispatch_semaphore_signal(信号量)
提高信号量
当返回值为0时表示当前并没有线程等待其处理的信号量,其处理的信号量的值加1即可。当返回值不为0时,表示其当前有(一个或多个)线程等待其处理的信号量,并且该函数唤醒了一个等待的线程(当线程有优先级时,唤醒优先级最高的线程;否则随机唤醒)。
# 3. dispatch_semaphore_wait
dispatch_semaphore_wait(信号量,等待时间)
这个函数会使传入的信号量dsema的值减1;
如果信号量的值 > 0,该函数所处线程就继续执行下面的语句,并且将信号量的值减1;
如果信号量的值 = 0,那么这个函数就阻塞当前线程等待 timeout(注意timeout的类型为dispatch_time_t,不能直接传入整形或float型数),如果等待的期间desema的值被dispatch_semaphore_signal
函数加1了,且该函数(即dispatch_semaphore_wait
)所处线程获得了信号量,那么就继续向下执行并将信号量减1。
如果等待期间没有获取到信号量或者信号量的值一直为0,那么等到timeout时,其所处线程自动执行其后语句。
在设置timeout时,比较有用的两个宏:DISPATCH_TIME_NOW
和 DISPATCH_TIME_FOREVER
。
DISPATCH_TIME_NOW // 表示当前;
DISPATCH_TIME_FOREVER // 表示遥远的未来;
# 二. 使用场景
在日常开发中经常会遇到 需要同时请求多个接口, 在多个接口同时请求完成时再做相关业务处理。
#pragma mark - 加载数据
- (void)loadDataSuccess:(void (^)(void))success
failure:(void (^)(NSString *errorMessage))failure {
// 3个接口,全部请求成功后刷新tableView
NSInteger totalCount = 3;
__block NSInteger requestCount = 0;
//初始化一个信号量 值为0
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
/// 请求1
// 请求成功时加入下面代码
// 请求成功 requestCount+1 后 与 totalCount 比较 相等时 说明3个请求都已经完成, 信号量加1
if (++requestCount == totalCount) {
dispatch_semaphore_signal(sem); // 提高信号量 信号量加1
}
/// 请求2
// 请求成功时加入下面代码
if (++requestCount == totalCount) {
dispatch_semaphore_signal(sem);
}
/// 请求3
// 请求成功时加入下面代码
if (++requestCount == totalCount) {
dispatch_semaphore_signal(sem);
}
// 等待降低信号量
// sem = 0 时,函数会阻塞当前线程 等待 timeout 后
// sem > 0 时,该函数所处线程会继续执行下面语句,并将信号量减1
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
// 信号量大于0时 会执行下面代码
dispatch_async(dispatch_get_main_queue(), ^{
/// 所有请求完成
// 实现回调
success();
});
});
}