转载请注明出处http://www.cnblogs.com/snailHL/p/3906112.html
我们在处理一系列线程的时候,当数量达到一定量,在以前我们可能会选择使用NSOperationQueue来处理并发控制,但如何在GCD中快速的控制并发呢?答案就是dispatch_semaphore
信号量是一个整形值并且具有一个初始计数值,并且支持两个操作:信号通知和等待。当一个信号量被信号通知,其计数会被增加。当一个线程在一个信号量上等待时,线程会被阻塞(如果有必要的话),直至计数器大于零,然后线程会减少这个计数。
在GCD中有三个函数是semaphore的操作,分别是:
方法 | 执行目标 |
---|---|
dispatch_semaphore_create | 创建一个semaphore |
dispatch_semaphore_signal | 发送一个信号 |
dispatch_semaphore_wait | 等待信号 |
三个函数介绍:
- 第一个函数:创建一个整形数值的信号,即:信号的总量
- 第二个函数:发送一个信号,让信号总量增加1
- 第三个函数:先把信号量减1,如果结果大于等于0则往下执行;否则阻塞等待
(1)dispatch_semaphore_create的声明为:
dispatch_semaphore_t dispatch_semaphore_create(long value);
dispatch_semaphore_t
dispatch_semaphore_create(long value)
{
dispatch_semaphore_t dsema;
// If the internal value is negative, then the absolute of the value is
// equal to the number of waiting threads. Therefore it is bogus to
// initialize the semaphore with a negative value.
//信号量初始化不能为负值
if (value < 0) {
return DISPATCH_BAD_INPUT;
}
dsema = _dispatch_object_alloc(DISPATCH_VTABLE(semaphore),
sizeof(struct dispatch_semaphore_s));
dsema->do_next = DISPATCH_OBJECT_LISTLESS;
dsema->do_targetq = _dispatch_get_default_queue(false);
dsema->dsema_value = value;
_dispatch_sema4_init(&dsema->dsema_sema, _DSEMA4_POLICY_FIFO);
dsema->dsema_orig = value;//原始信号值
return dsema;
}
传入的参数为long,输出一个dispatch_semaphore_t类型且值为value的信号量。
值得注意的是,这里的传入的参数value必须大于或等于0,否则dispatch_semaphore_create会返回NULL。
(关于信号量,我就不在这里累述了,网上很多介绍这个的。我们这里主要讲一下dispatch_semaphore这三个函数的用法)。
(2)dispatch_semaphore_signal的声明为:
long dispatch_semaphore_signal(dispatch_semaphore_t dsema)
这个函数会使传入的信号量dsema的值加1;(至于返回值,待会儿再讲)
long
dispatch_semaphore_signal(dispatch_semaphore_t dsema) {
//先把信号量加1,
long value = os_atomic_inc2o(dsema, dsema_value, release);
//如果大于0 说明没有等待。(由于等待的时候先减1,使得信号量变为-1,所以再加1后必定变为0) 此时是有等待的)
if (likely(value > 0)) {
return 0;
}
if (unlikely(value == LONG_MIN)) {
DISPATCH_CLIENT_CRASH(value,
"Unbalanced call to dispatch_semaphore_signal()");
}
return _dispatch_semaphore_signal_slow(dsema);
}
(3) dispatch_semaphore_wait的声明为:
long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
这个函数会使传入的信号量dsema的值减1;
long
dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout)
{
/*
#define os_atomic_dec2o(p, f, m) \
os_atomic_sub2o(p, f, 1, m)
*/
//
long value = os_atomic_dec2o(dsema, dsema_value, acquire);
if (likely(value >= 0)) {
return 0;
}
return _dispatch_semaphore_wait_slow(dsema, timeout);
}
先把信号量减1,如果结果大于等于0则往下执行;否则阻塞当前线程等待timeout
(4)dispatch_semaphore_signal的返回值为long类型,当返回值为0时表示当前并没有线程等待其处理的信号量,其处理
的信号量的值加1即可。当返回值不为0时,表示其当前有(一个或多个)线程等待其处理的信号量,并且该函数唤醒了一
个等待的线程(当线程有优先级时,唤醒优先级最高的线程;否则随机唤醒)。
dispatch_semaphore_wait的返回值也为long型。当其返回0时表示在timeout之前,该函数所处的线程被成功唤醒。
当其返回不为0时,表示timeout发生。
(5)在设置timeout时,比较有用的两个宏:DISPATCH_TIME_NOW 和 DISPATCH_TIME_FOREVER。
DISPATCH_TIME_NOW 表示当前;
DISPATCH_TIME_FOREVER 表示遥远的未来;
一般可以直接设置timeout为这两个宏其中的一个,或者自己创建一个dispatch_time_t类型的变量。
创建dispatch_time_t类型的变量有两种方法,dispatch_time和dispatch_walltime。
利用创建dispatch_time创建dispatch_time_t类型变量的时候一般也会用到这两个变量。
dispatch_time的声明如下:
dispatch_time_t dispatch_time(dispatch_time_t when, int64_t delta);
其参数when需传入一个dispatch_time_t类型的变量,和一个delta值。表示when加delta时间就是timeout的时间。
例如:dispatch_time_t t = dispatch_time(DISPATCH_TIME_NOW, 110001000*1000);
表示当前时间向后延时一秒为timeout的时间。
(6)关于信号量,一般可以用停车来比喻。
停车场剩余4个车位,那么即使同时来了四辆车也能停的下。如果此时来了五辆车,那么就有一辆需要等待。
信号量的值就相当于剩余车位的数目,
dispatch_semaphore_wait函数就相当于来了一辆车,dispatch_semaphore_signal就相当于走了一辆车。停车位的剩余数目在初始化的时候就已经指明了(dispatch_semaphore_create(long value)),
调用一次dispatch_semaphore_signal,剩余的车位就增加一个;调用一次dispatch_semaphore_wait剩余车位就减少一个;
当剩余车位为0时,再来车(即调用dispatch_semaphore_wait)就只能等待。有可能同时有几辆车等待一个停车位。有些车主
没有耐心,给自己设定了一段等待时间,这段时间内等不到停车位就走了,如果等到了就开进去停车。而有些车主就像把车停在这,
所以就一直等下去。
代码示例如下:
dispatch_semaphore_t signal = dispatch_semaphore_create(1);//创建一个为1信号量的信号
__block long x = 0;
NSLog(@"0_x:%ld",x);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
NSLog(@"waiting");
x = dispatch_semaphore_signal(signal);//此时信号量为0 对signal增加1 信号量变为1,
NSLog(@"1_x:%ld",x);
sleep(2);//睡两秒
NSLog(@"waking");
x = dispatch_semaphore_signal(signal);
NSLog(@"2_x:%ld",x);
});
// dispatch_time_t duration = dispatch_time(DISPATCH_TIME_NOW, 1*1000*1000*1000); //超时1秒
// dispatch_semaphore_wait(signal, duration);
x = dispatch_semaphore_wait(signal, DISPATCH_TIME_FOREVER);//此时信号量为1 所以执行下边,切对signal减掉1.然后信号量为0
NSLog(@"3_x:%ld",x);
x = dispatch_semaphore_wait(signal, DISPATCH_TIME_FOREVER);///此时信号量为0,永远等待,在等待的时候执行block了,在等待block时候block内对信号量增加了1,然后开始执行下边,并且信号量再次减掉1 变为0
NSLog(@"wait 2");
NSLog(@"4_x:%ld",x);
x = dispatch_semaphore_wait(signal, DISPATCH_TIME_FOREVER);//此时信号量为0,永远等待,在等待的时候执行block了,在等待block时候block内对信号量增加了1,然后开始执行下边,并且信号量再次减掉1 变为0
NSLog(@"wait 3");
NSLog(@"5_x:%ld",x);
代码执行结果:
// [84650:1805821] 0_x:0
// [84650:1805821] 3_x:0
// [84650:1805912] waiting
// [84650:1805912] 1_x:1
// [84650:1805821] wait 2
// [84650:1805821] 4_x:0
// [84650:1805912] waking
// [84650:1805912] 2_x:1
// [84650:1805821] wait 3
// [84650:1805821] 5_x:0