今天跟大家聊聊关于 GCD 中的信号量的使用.
网上有很多关于信号量的介绍, 这里只是结合本人一些理解和实际使用来分享给大家.
在GCD中有三个函数是semaphore的操作,分别是
1.创建一个semaphore
dispatch_semaphore_create
2. 发送一个信号
dispatch_semaphore_signal
会使信号量计数增加1.
3. 等待信号
dispatch_semaphore_wait
会使信号量计数减少1.
/usr/include/dispatch/semaphore.h 中关于函数的声明和定义, 注释很清晰.
这里特别需要注意的几个问题:
- 创建信号量的函数
dispatch_semaphore_t dispatch_semaphore_create(long value);
函数需要传入一个大于或者等于0的值, 传入小于0的值, 函数返回 NULL.- 等待信号函数
long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
返回0表示成功, 非0表示超时, 会继续执行该函数后面的语句.
关于参数 timeout, 后面细说.- 发送信号函数
long dispatch_semaphore_signal(dispatch_semaphore_t dsema);
返回非0值表示成功唤醒线程, 0表示没有唤醒.
下面结合实际应用场景, 说说信号量的使用和注意事项.
在开发过程中, 大家应该会有这样的需求
场景1:
等待上一个网络请求的返回结果, 再决定是否进行下一个网络请求.
示例代码:
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[NetReq request:^(BOOL ok) {
// 唤醒等待的线程
dispatch_semaphore_signal(sema);
} onFail:^(int errorCode, NSString *errorMessage) {
// 唤醒等待的线程
dispatch_semaphore_signal(sema);
}];
// 一直等待
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
// 等待上面网络请求结束, 开始执行下面语句
[NetReq request...]
场景2:
生产者和消费者问题
使用信号量可以解决该类型的问题.
示例代码
dispatch_semaphore_t signal = dispatch_semaphore_create(0);
if (NULL == signal) {
return;
}
//消费者队列
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (true) {
// 在超时时间内成功得到信号触发返回0值表示成功, 非0表示失败
// 超时时间设置为DISPATCH_TIME_FOREVER, 表示一直等待, 其后语句被阻塞, 一直等到其被唤醒
// DISPATCH_TIME_NOW, 表示不等待, 继续执行其后面的语句
// 也可以指定时间, dispatch_time(DISPATCH_TIME_NOW, 10*NSEC_PER_SEC)), 表示10s内等待, 超过10s, 继续执行下面语句
long result = dispatch_semaphore_wait(signal, DISPATCH_TIME_NOW);
// 超时了
if (result != 0) {
continue;
}
// 被唤醒
NSLog(@"1....消费 %@", self.lockData);
[self.lockData removeObjectAtIndex:0];
}
});
// 这里可以阻塞一段时间, 延时增加信号量来看看消费者队列中 wait 的情况
sleep(5);
//生产者队列
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (true) {
[self.lockData addObject:@"0"];
NSLog(@"0-------产出: %@", self.lockData);
// 使信号量加1, 返回非0表示成功, 0表示失败
long result = dispatch_semaphore_signal(signal);
//失败
if (0 == result) {
//wait for a while
sleep(1);
continue;
}
//成功
//TODO:
}
});
关于超时参数的定义在注释中有详细说明.
现在你可以动手试一试了.