-
dispatch_semaphore 一共有三个方法
dispatch_semaphore_create => 创建一个信号量
dispatch_semaphore_signal => 发送一个信号
dispatch_semaphore_wait => 等待信号
关于信号量,一般可以用停车来比喻。
停车场剩余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 semaphore = dispatch_semaphore_create(0);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"等待semaphore");
结果就是, 等待semaphore 这句话永远不会输出。原因有两个
- 你初始化信号量的时候,并没有库存,也就是你传入的值是0.
- 你传入等待增加库存的时间是 DISPATCH_TIME_FOREVER ,也就是说,除非有 地方运行了 dispatch_semaphore_signal 增加了库存,否则我永远等待下 去。 基于上述的两个原因,导致了程序不往下走了。
-
信号量的应用
1.测试异步网络请求
在编写单元测试中就可以:
- (void)downloadImageURLWithString:(NSString *)URLString
{
// 1
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0)
NSURL *url = [NSURL URLWithString:URLString];
__unused Photo *photo = [[Photo alloc] initwithURL:url withCompletionBlock:^(UIImage *image, NSError *error) {
if (error) {
XCTFail(@"%@ failed. %@", URLString, error);
}
// 2
dispatch_semaphore_signal(semaphore);
}];
// 3
dispatch_time_t timeoutTime = dispatch_time(DISPATCH_TIME_NOW, 5);
if (dispatch_semaphore_wait(semaphore, timeoutTime)) {
XCTFail(@"%@ timed out", URLString);
}
}
2.控制并发量
@implementation CustomOperationQueue
- (id)initWithConcurrentCount:(int)count
{
self = [super init];
if (self) {
if (count < 1) count = 5;
semaphore = dispatch_semaphore_create(count);
queue = Dispatch_queue_create("Jake", DISPATCH_QUEUE_CONCURRENT);
}
return self;
}
- (id)init
{
return [self initWithConcurrentCount:5];
}
- (void)addTask:(CallBackBlock)block
{
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), 0), ^{
block();
dispatch_semaphore_signal(semaphore);
});
});
}
@end
在 addTask:
方法中
1.当前的初始库存为5
2.第一次调用dispatch_semaphore_wait
的时候会直接执行下去,并减少一个库存。每当完成一个任务之后,会执行到dispatch_semaphore_signal
将库存添加回去。
3.当执行的任务为非常耗时的操作的时候,库存不能及时地还回去。而dispatch_semaphore_wait
在仍然执行,库存最后会被减到0,这样dispatch_semaphore_wait
就只能进行等待直到前面的任务有执行完成将存库有添加回去为止。
如此便完成了并发量的控制!
线程锁的使用:
以下代码来自YYModel
+ (instancetype)classInfoWithClass:(Class)cls {
if (!cls) return nil;
static CFMutableDictionaryRef classCache;
static CFMutableDictionaryRef metaCache;
static dispatch_once_t onceToken;
static dispatch_semaphore_t lock;
dispatch_once(&onceToken, ^{
classCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
metaCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
lock = dispatch_semaphore_create(1);
});
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
YYClassInfo *info = CFDictionaryGetValue(class_isMetaClass(cls) ? metaCache : classCache, (__bridge const void *)(cls));
if (info && info->_needUpdate) {
[info _update];
}
dispatch_semaphore_signal(lock);
if (!info) {
info = [[YYClassInfo alloc] initWithClass:cls];
if (info) {
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
CFDictionarySetValue(info.isMeta ? metaCache : classCache, (__bridge const void *)(cls), (__bridge const void *)(info));
dispatch_semaphore_signal(lock);
}
}
return info;
}
网友评论