什么是dispatch_semaphore(信号量)?
以一个停车场的运作为例。简单起见,假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了五辆车,看 门人允许其中三辆直接进入,然后放下车拦,剩下的车则必须在入口等待,此后来的车也都不得不在入口处等待。这时,有一辆车离开停车场,看门人得知后,打开 车拦,放入外面的一辆进去,如果又离开两辆,则又可以放入两辆,如此往复。在这个停车场系统中,车位是公共资源,每辆车好比一个线程,看门人起的就是信号量的作用。
言归正传:
dispatch_semaphore主要的三个方法
GCD信号量机制主要涉及到以下三个函数:
// 创建信号量,value:初始信号量数 如果小于0则会返回NULL
dispatch_semaphore_create(long value);
// 发送信号量是的信号量+1
dispatch_semaphore_signal(dispatch_semaphore_t deem);
//当信号量为0时候,会阻塞当前线程 参数(信号量,等待时间)
dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
dispatch_semaphore主要作用
- 异步转同步
众所周知并发队列中的任务,由异步线程执行的顺序是不确定的,两个任务分别由两个线程执行,很难控制哪个任务先执行完,哪个任务后执行完。但有时候确实有这样的需求:两个任务虽然是异步的,但仍需要同步执行。
//开启异步执行三个操作
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
sleep(1);
NSLog(@"执行任务: A");
});
dispatch_async(queue, ^{
sleep(1);
NSLog(@"执行任务: B");
});
dispatch_async(queue, ^{
sleep(1);
NSLog(@"执行任务: C");
});
执行多次结果表明:A,B,C都是异步的,谁前谁后无法确定。所以可以使用dispatch_semaphore,让异步变同步,让上面的执行顺序是A,B,C.
//信号量初始化必须大于等于0, 因为dispatch_semaphore_wait 执行的是-1操作。
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
//创建异步队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
sleep(1);
NSLog(@"执行任务: A");
//让信号量+1
dispatch_semaphore_signal(semaphore);
});
//当当前的信号量值为0时候会阻塞线,如果大于0的话,信号量-1,不阻塞线程.(相当于加锁)
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_async(queue, ^{
sleep(1);
NSLog(@"执行任务: B");
//让信号量+1(相当于解锁)
dispatch_semaphore_signal(semaphore);
});
//当当前的信号量值为0时候会阻塞线,如果大于0的话,信号量-1,不阻塞线程
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_async(queue, ^{
sleep(1);
NSLog(@"执行任务: C");
dispatch_semaphore_signal(semaphore);
});
多次运行的结果都是A,B,C顺序执行,让A,B,C异步执行变成同步执行,dispatch_semaphore相当于加锁效果
- 设置最大开辟的线程数
我们要下载很多图片,并发异步进行,每个下载都会开辟一个新线程,可是我们又担心太多线程肯定cpu吃不消,那么我们这里也可以用信号量控制一下最大开辟线程数。
//设置最大开辟的线程数为3
dispatch_semaphore_t semaphore = dispatch_semaphore_create(3);
//创建一个并发队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//开启的是10个异步的操作,通过信号量,让10个异步的最多3个m,剩下的同步等待
for (NSInteger i = 0; i < 10; i++) {
dispatch_async(queue, ^{
//当信号量为0时候,阻塞当前线程
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"执行任务 %ld", i);
sleep(1);
NSLog(@"完成当前任务 %ld", i);
//释放信号
dispatch_semaphore_signal(semaphore);
});
}
执行结果:
执行任务 0
执行任务 2
执行任务 1
完成当前任务 2
完成当前任务 1
完成当前任务 0
执行任务 3
执行任务 4
执行任务 5
完成当前任务 3
完成当前任务 5
完成当前任务 4
执行任务 6
执行任务 8
执行任务 7
完成当前任务 7
完成当前任务 8
完成当前任务 6
执行任务 9
完成当前任务 9
执行结果表明:最开始执行3个异步操作,剩下的同步等待,只有释放出来才可以剩下的操作才可以进行异步操作
- 加锁机制
当你的信号设置为1的时候就相当于加锁
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"semaphore---begin");
semaphoreLock = dispatch_semaphore_create(1);
self.ticketCount = 50;
// queue1 代表北京火车票售卖窗口
dispatch_queue_t queue1 = dispatch_queue_create("net.bujige.testQueue1", DISPATCH_QUEUE_SERIAL);
// queue2 代表上海火车票售卖窗口
dispatch_queue_t queue2 = dispatch_queue_create("net.bujige.testQueue2", DISPATCH_QUEUE_SERIAL);
__weak typeof(self) weakSelf = self;
dispatch_async(queue1, ^{
[weakSelf saleTicketSafe];
});
dispatch_async(queue2, ^{
[weakSelf saleTicketSafe];
});
/**
* 售卖火车票(线程安全)
*/
- (void)saleTicketSafe {
while (1) {
// 相当于加锁
dispatch_semaphore_wait(semaphoreLock, DISPATCH_TIME_FOREVER);
if (self.ticketCount > 0) {
//如果还有票,继续售卖
self.ticketSurplusCount--;
NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%d 窗口:%@", self.ticketSurplusCount, [NSThread currentThread]]);
[NSThread sleepForTimeInterval:0.2];
} else {
//如果已卖完,关闭售票窗口
NSLog(@"所有火车票均已售完");
// 相当于解锁
dispatch_semaphore_signal(semaphoreLock);
break;
}
// 相当于解锁
dispatch_semaphore_signal(semaphoreLock);
}
}
可以看出,在考虑了线程安全的情况下,使用 dispatch_semaphore 机制之后,得到的票数是正确的,没有出现混乱的情况。我们也就解决了多个线程同步的问题。
原文链接:枫叶无处漂泊
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
网友评论