信号量是基于计数器的一种多线程同步机制,用来管理对资源的并发访问。
信号量内部有一个可以原子递增或递减的值。如果一个动作尝试减少信号量的值,使其小于0,那么这个动作将会被阻塞,直到有其他调用者(在其他线程中)增加该信号量的值。
信号量就是一种可用来控制访问资源的数量的标识,设定了一个信号量,在线程访问之前,加上信号量的处理,则可告知系统按照我们指定的信号量数量来执行多个线程。
有点类似锁机制,只不过信号量都是系统帮助我们处理,只需要在执行线程之前,设定一个信号量值,并且在使用时,加上信号量处理方法就行了。
简单来讲 信号量为0则阻塞线程,大于0则不会阻塞。则我们通过改变信号量的值,来控制是否阻塞线程,从而达到线程同步。
dispatch_semaphore相关的3个函数
-
dispatch_semaphore_create
创建一个Semaphore并初始化信号的总量
/*!
* @function dispatch_semaphore_create
* @abstract 创建一个新的信号量
* @param value 信号量的初始值,传入小于0的数返回NULL
*/
dispatch_semaphore_t
dispatch_semaphore_create(long value);
-
dispatch_semaphore_wait
可以使总信号量减1,当信号总量为0时就会一直等待(阻塞所在线程),否则就可以正常执行。
/*!
* @function dispatch_semaphore_wait
*
* @abstract 等待信号量
* Wait (decrement) for a semaphore.
* @param dsema 创建好的信号量
* @param timeout 信号等待时间,一般使用DISPATCH_TIME_FOREVER,在得到signal之前一直等待
*/
dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
-
dispatch_semaphore_signal
发送一个信号,让信号总量加1
/*!
* @function dispatch_semaphore_signal
*
* @abstract 提高信号量, 使信号量加1并返回
* @param dsema 已创建好的信号
*/
dispatch_semaphore_signal(dispatch_semaphore_t dsema);
dispatch_semaphore主要应用于两个方面
-
线程同步
直接上代码
- (void)semaphoreTest {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
__block long j = 0;
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 1000; i ++) {
j ++;
}
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"%ld",j);
}
//运行结果:2018-05-23 17:56:48.064 MultiThreadingDemo[2955:535470] 1000
代码说明:block块异步执行添加到了全局并发队列里,所以程序在主线程会跳过block块(同时开辟子线程异步执行block块),执行块外的代码dispatch_semaphore_wait,因为semaphore信号量为0,且时间为DISPATCH_TIME_FOREVER,所以会阻塞当前线程(主线程),进而只执行子线程的block块,直到执行块内部的dispatch_semaphore_signal使得信号量+1。正在被阻塞的线程(主线程)会恢复继续执行。这样保证了线程之间的同步。
-
为线程加锁,限制并发线程数量
使用dispatch_semaphore_create(0);
保证在同一时间只有一个线程访问,实现加锁功能。关于限制线程数量,我们用一个停车场的demo看一下:
//停车场demo
- (void)parkingAreaADemo {
//假设目前有3个停车位
dispatch_semaphore_t semaphore = dispatch_semaphore_create(3);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//有10辆车过来打算停车
for (NSInteger i = 1; i <= 10; i ++) {
dispatch_async(queue, ^{
NSInteger carId = i;
if (carId % 3 == 0) {
//这几位车主不愿意一直等待,所有设定一个能接受的等待时间
NSUInteger result = dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 8 * carId * NSEC_PER_SEC));
if (result != 0) {//超时,直接离开
NSLog(@"第%ld个车主不等了",carId);
} else {
NSLog(@"第%ld个车主在规定的时间内等到了车位,进入停车场",carId);
[NSThread sleepForTimeInterval:10];
dispatch_semaphore_signal(semaphore);
NSLog(@"第%ld个车主离开,有空位了",carId);
}
} else {
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"第%ld个车主进入停车场",carId);
[NSThread sleepForTimeInterval:10 + i * 10];
dispatch_semaphore_signal(semaphore);
NSLog(@"第%ld个车主离开,有空位了",carId);
}
});
}
}
//运行结果
2018-05-24 12:13:55.959 MultiThreadingDemo[1490:186763] 第3个车主离开,有空位了
2018-05-24 12:13:55.959 MultiThreadingDemo[1490:186860] 第4个车主进入停车场
2018-05-24 12:14:05.959 MultiThreadingDemo[1490:186764] 第1个车主离开,有空位了
2018-05-24 12:14:05.959 MultiThreadingDemo[1490:186861] 第5个车主进入停车场
2018-05-24 12:14:15.960 MultiThreadingDemo[1490:186766] 第2个车主离开,有空位了
2018-05-24 12:14:15.960 MultiThreadingDemo[1490:186862] 第6个车主在规定的时间内等到了车位,进入停车场
2018-05-24 12:14:25.962 MultiThreadingDemo[1490:186862] 第6个车主离开,有空位了
2018-05-24 12:14:25.962 MultiThreadingDemo[1490:186863] 第7个车主进入停车场
2018-05-24 12:14:45.966 MultiThreadingDemo[1490:186860] 第4个车主离开,有空位了
2018-05-24 12:14:45.966 MultiThreadingDemo[1490:186864] 第8个车主进入停车场
2018-05-24 12:14:57.961 MultiThreadingDemo[1490:186865] 第9个车主不等了
2018-05-24 12:15:05.964 MultiThreadingDemo[1490:186861] 第5个车主离开,有空位了
2018-05-24 12:15:05.964 MultiThreadingDemo[1490:186866] 第10个车主进入停车场
2018-05-24 12:15:45.968 MultiThreadingDemo[1490:186863] 第7个车主离开,有空位了
2018-05-24 12:16:15.972 MultiThreadingDemo[1490:186864] 第8个车主离开,有空位了
2018-05-24 12:16:55.971 MultiThreadingDemo[1490:186866] 第10个车主离开,有空位了
GCD系列
参考资料:
https://blog.csdn.net/liuyang11908/article/details/70757534
https://www.jianshu.com/p/2d57c72016c6