-
dispatch_semaphore 一共有三個方法
dispatch_semaphore_create => 創(chuàng)建一個信號量
dispatch_semaphore_signal => 發(fā)送一個信號
dispatch_semaphore_wait => 等待信號
關(guān)于信號量,一般可以用停車來比喻。
停車場剩余4個車位,那么即使同時來了四輛車也能停的下。如果此時來了五輛車,那么就有一輛需要等待。
信號量的值就相當(dāng)于剩余車位的數(shù)目,dispatch_semaphore_wait
函數(shù)就相當(dāng)于來了一輛車,dispatch_semaphore_signal
就相當(dāng)于走了一輛車。停車位的剩余數(shù)目在初始化的時候就已經(jīng)指明了(dispatch_semaphore_create(long value)
),
調(diào)用一次dispatch_semaphore_signal
,剩余的車位就增加一個;調(diào)用一次dispatch_semaphore_wait
剩余車位就減少一個;
當(dāng)剩余車位為0時,再來車(即調(diào)用dispatch_semaphore_wait
)就只能等待。有可能同時有幾輛車等待一個停車位。有些車主沒有耐心,給自己設(shè)定了一段等待時間,這段時間內(nèi)等不到停車位就走了,如果等到了就開進(jìn)去停車。而有些車主就像把車停在這,所以就一直等下去。
代碼示例:
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"等待semaphore");
結(jié)果就是, 等待semaphore 這句話永遠(yuǎn)不會輸出。原因有兩個
- 你初始化信號量的時候,并沒有庫存,也就是你傳入的值是0.
- 你傳入等待增加庫存的時間是 DISPATCH_TIME_FOREVER ,也就是說,除非有 地方運行了 dispatch_semaphore_signal 增加了庫存,否則我永遠(yuǎn)等待下 去。 基于上述的兩個原因,導(dǎo)致了程序不往下走了。
-
信號量的應(yīng)用
1.測試異步網(wǎng)絡(luò)請求
在編寫單元測試中就可以:
- (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.控制并發(fā)量
@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.當(dāng)前的初始庫存為5
2.第一次調(diào)用dispatch_semaphore_wait
的時候會直接執(zhí)行下去,并減少一個庫存。每當(dāng)完成一個任務(wù)之后,會執(zhí)行到dispatch_semaphore_signal
將庫存添加回去。
3.當(dāng)執(zhí)行的任務(wù)為非常耗時的操作的時候,庫存不能及時地還回去。而dispatch_semaphore_wait
在仍然執(zhí)行,庫存最后會被減到0,這樣dispatch_semaphore_wait
就只能進(jìn)行等待直到前面的任務(wù)有執(zhí)行完成將存庫有添加回去為止。
如此便完成了并發(fā)量的控制!
線程鎖的使用:
以下代碼來自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;
}