dispatch_semaphore
,一般都称之为信号量,功能类似于OC中的锁(比如NSLock
,@synchronized
等),是一种基于计数器的多线程同步机制。dispatch_semaphore
对象适用于控制一个仅支持有限个用户的共享资源,是一种不需要使用忙碌等待(busy waiting)的一种方法。
简介
dispatch_semaphore有三个常用函数:
dispatch_semaphore_create
dispatch_semaphore_wait
dispatch_semaphore_signal
dispatch_semaphore_create
/*! * @function dispatch_semaphore_create * * @abstract * Creates new counting semaphore with an initial value. * * @discussion * Passing zero for the value is useful for when two threads need to reconcile * the completion of a particular event. Passing a value greater than zero is * useful for managing a finite pool of resources, where the pool size is equal * to the value. * * @param value * The starting value for the semaphore. Passing a value less than zero will * cause NULL to be returned. * * @result * The newly created semaphore, or NULL on failure. */ dispatch_semaphore_t dispatch_semaphore_create(long value);
可使用如下方法创建信号量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(long value)
这行代码:传入一个long类型大于等于0的value(小于0会返回NULL),返回一个dispatch_semaphore_t
类型的信号量对象semaphore
。
dispatch_semaphore_wait
/*! * @function dispatch_semaphore_wait * * @abstract * Wait (decrement) for a semaphore. * * @discussion * Decrement the counting semaphore. If the resulting value is less than zero, * this function waits for a signal to occur before returning. * * @param dsema * The semaphore. The result of passing NULL in this parameter is undefined. * * @param timeout * When to timeout (see dispatch_time). As a convenience, there are the * DISPATCH_TIME_NOW and DISPATCH_TIME_FOREVER constants. * * @result * Returns zero on success, or non-zero if the timeout occurred. */ long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
等待信号量,每执行一次,会对信号量semaphore
的值减1,返回long类型的value。官方文档对返回值的解释是Returns zero on success, or non-zero if the timeout occurred
,也就是说返回0,执行成功;非0,执行超时。当semaphore
的值为0时,当前线程就会被阻塞直到timeout,timeout有DISPATCH_TIME_NOW
和DISPATCH_TIME_FOREVER
,也可以自定义(通过dispatch_time和dispatch_walltime)。
dispatch_semaphore_signal
/*! * @function dispatch_semaphore_signal * * @abstract * Signal (increment) a semaphore. * * @discussion * Increment the counting semaphore. If the previous value was less than zero, * this function wakes a waiting thread before returning. * * @param dsema The counting semaphore. * The result of passing NULL in this parameter is undefined. * * @result * This function returns non-zero if a thread is woken. Otherwise, zero is * returned. */ long dispatch_semaphore_signal(dispatch_semaphore_t dsema);
dispatch_semaphore_signal
是发送一个信号,会给信号量对象semaphore
的值加1。返回值的意义:如果线程被唤醒了,返回非0;否则返回0。
关于dispatch_semaphore
,有一篇博客比喻的非常形象:停车场剩余4个车位,那么即使同时来了四辆车也能停的下。如果此时来了五辆车,那么就有一辆需要等待。信号量的值就相当于剩余车位的数目,dispatch_semaphore_wait
函数就相当于来了一辆车,dispatch_semaphore_signal
就相当于走了一辆车。停车位的剩余数目在初始化的时候就已经指明了(dispatch_semaphore_create(long value)
),调用一次dispatch_semaphore_signal
,剩余的车位就增加一个;调用一次dispatch_semaphore_wait
剩余车位就减少一个;当剩余车位为0时,再来车(即调用dispatch_semaphore_wait
)就只能等待。有可能同时有几辆车等待一个停车位。有些车主没有耐心,给自己设定了一段等待时间,这段时间内等不到停车位就走了,如果等到了就开进去停车。而有些车主就像把车停在这,所以就一直等下去。
使用
dispatch_semaphor
其实就是一种带有数量控制的互斥锁,因此我们可以在多个线程同时访问某块共享资源的时候用其来保证线程安全,避免出现数据不一致或数据污染等问题。
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
self.array = [NSMutableArray array];
for (NSInteger i = 0; i < 100; i++) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW);
[self.array addObject:@(self.count)];
self.count++;
dispatch_semaphore_signal(semaphore);
});
}
如果不加dispatch_semaphor
的话,输出的array数组中就不能保证正确的顺序输出0,1,2,3,4,5...
。去掉锁后试了一下,因为异步执行,如果self.count++
尚未执行,而另一个线程已经addObject
了,就会导致数组中出现重复的内容。