- (void)semaphoreaTest {
NSLog(@"semaphore---begin");
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:2];
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"semaphore---end");
}
关于dispatch_semaphore_t
,网上的文章讲的都是
dispatch_semaphore_wait
先会使semaphore count
- 1,如果 - 1后的semaphore count
>= 0,则放行,不然则阻塞当前线程,而dispatch_semaphore_signal
只起到了semaphore count
+ 1的作用。
事实是这样吗,dispatch_semaphore_signal
官方文档解释是:
Increment the counting semaphore. If the previous value was less than zero, this function wakes a thread currently waiting in dispatch_semaphore_wait
意思是dispatch_semaphore_signal
会先让semaphore count
+ 1,如果+1前的semaphore count
是 < 0的,则直接唤醒正在dispatch_semaphore_wait
中等待的线程,所以我理解的是
dispatch_semaphore_wait
只是起到了通过semaphore count
判断是否需要阻塞当前线程并记录当前的线程的作用,当dispatch_semaphore_signal
需要唤醒线程时,会根据dispatch_semaphore_wait
记录的线程进行唤醒。
再加一个案例解释,火车票线程安全访问问题:
- (void)semaphoreThreadSafe {
[self logCurrentThreadInfo:nil];
NSLog(@"semaphore---begin");
semaphore = dispatch_semaphore_create(1);
ticketSurplusCount = 50;
//queue1 1号窗口
dispatch_queue_t queue1 = dispatch_queue_create("one.window.queue", DISPATCH_QUEUE_SERIAL);
//queue2 2号窗口
dispatch_queue_t queue2 = dispatch_queue_create("two.window.queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue1, ^{
[self saleTicketSafe];
});
dispatch_async(queue2, ^{
[self saleTicketSafe];
});
}
- (void)saleTicketSafe {
while (1) {
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
if (ticketSurplusCount > 0) { // 如果还有票,继续售卖
ticketSurplusCount--;
NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%ld 窗口:%@", ticketSurplusCount, [NSThread currentThread]]);
[NSThread sleepForTimeInterval:0.2];
dispatch_semaphore_signal(semaphore);
} else { // 如果已卖完,关闭售票窗口
NSLog(@"所有火车票均已售完");
// 相当于解锁
dispatch_semaphore_signal(semaphore);
break;
}
}
}
线程1
:1号窗口所在的线程
线程2
:2号窗口所在线程
semaphore count
:信号量计数
●线程1
访问ticketSurplusCount
,通过dispatch_semaphore_wait
将semaphore count
- 1,此时semaphore count
= 0,不需要阻塞线程1
;
●线程1
还没对ticketSurplusCount
访问完成时,线程2
突然也要访问,经过dispatch_semaphore_wait
时,semaphore count
- 1,此时semaphore count
= -1,需要阻塞线程2
,线程2
变为阻塞(等待)状态;
● 此时线程1
对ticketSurplusCount
访问完成,调用dispatch_semaphore_signal
使semaphore count
+ 1,现在的semaphore count
= 0,然后由于semaphore count
在 + 1前是 < 0 的,所以需要唤醒线程2
●线程2
现在可以正常访问了
网友评论