異步Block接口轉(zhuǎn)為同步接口
1.異步Block接口
通常在遇到本地IO操作,網(wǎng)絡(luò)請求等情況,我們習(xí)慣于使用Block作為回調(diào),獲取操作的結(jié)果,比如:
doSomething({
// success
},{
// failuer
})
但是當(dāng)業(yè)務(wù)復(fù)雜,需要處理的異步任務(wù)比較多時(shí),就會出現(xiàn)回調(diào)地獄的問題:
doSomething({
doSomething({
doSomething({
doSomething({
doSomething({
// =======================
})
})
})
})
})
基于這個(gè)問題,出現(xiàn)了一些解決方案,使用比較多的,就是Promise,將這種"粽子"調(diào)用改成"寬面"的調(diào)用:
doSomething()
.then({
// =======================
})
.then({
// =======================
})
.thern({
// =======================
})
.thern({
// =======================
})
.thern({
// =======================
})
但是這種代碼還是不夠優(yōu)雅,于是又有了async/await的方案:
await doSomething();
await doSomething();
await doSomething();
await doSomething();
await doSomething();
這個(gè)就是我們理想中的代碼。
PS:以上代碼為偽碼
2.iOS 將 block 轉(zhuǎn)為同步
理想很豐滿,現(xiàn)實(shí)很骨感。Objective-C 沒有提供 async/await 方案。如果自己擼一套,由于沒有編譯器支持,自己添加語法糖的話,會把代碼搞的更丑,因此我嘗試直接將 Block 轉(zhuǎn)為同步。
原理很簡單,就是在 block 回調(diào)時(shí),做一次線程同步:
改造前:
- (void)sendMessage:(NSString *)message callback:(MissionCallback)callback {
float random = 1 + (arc4random()%100)/30.0;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(random * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSString *tag = [NSString stringWithFormat:@"%@ - random:%f", message, random];
callback(tag);
});
}
改造后
- (NSString *)sendMessageSemaphore:(NSString *)message {
__block NSString *resultMessage;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[self sendMessage:message callback:^(NSString * _Nonnull result) {
resultMessage = result;
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
return resultMessage;
}
代碼很簡單,最終的效果也差強(qiáng)人意。實(shí)測修改后,該接口同時(shí)支持63方并發(fā)訪問,再多就會卡死。
至于為什么是63這個(gè)數(shù)字,是因?yàn)間cd最大線程數(shù)是63。當(dāng)使用gcd創(chuàng)建了64個(gè)線程并發(fā)時(shí),再申請線程資源會被阻塞,直到有其他線程資源被釋放。而本例中由于進(jìn)行線程同步需要占用當(dāng)前線程,因此當(dāng)有64個(gè)任務(wù)并發(fā)時(shí),就無法再創(chuàng)建callback的線程,導(dǎo)致所有任務(wù)卡死。
附 xnu 地址:https://github.com/apple/darwin-xnu
3.Block 轉(zhuǎn)同步的其他姿勢
試驗(yàn)中,分別用信號量、條件鎖、互斥鎖、自旋鎖寫了demo,最終的實(shí)測效果和上面一樣。性能上,dispatch_semaphore 和 pthread_mutex 性能比較好;OSSpinLock 不再安全已經(jīng)廢棄;NSCondition/NSConditionLock 更加靈活一些,Objective-C的接口對一些不熟悉c的程序員更友好。
以下是幾種不同的方法的代碼實(shí)現(xiàn):
// Block
- (void)sendMessage:(NSString *)message callback:(MissionCallback)callback {
float random = 1 + (arc4random()%100)/30.0;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(random * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSString *tag = [NSString stringWithFormat:@"%@ - random:%f", message, random];
callback(tag);
});
}
// 信號量
- (NSString *)sendMessageSemaphore:(NSString *)message {
__block NSString *resultMessage;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[self sendMessage:message callback:^(NSString * _Nonnull result) {
resultMessage = result;
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
return resultMessage;
}
// 條件鎖
- (NSString *)sendMessageCondition:(NSString *)message {
__block NSString *resultMessage;
NSCondition *condition = [[NSCondition alloc] init];
[condition lock];
[self sendMessage:message callback:^(NSString *result) {
resultMessage = result;
[condition signal];
}];
[condition wait];
[condition unlock];
return resultMessage;
}
- (NSString *)sendMessageConditionLock:(NSString *)message {
__block NSString *resultMessage;
NSConditionLock *lock = [[NSConditionLock alloc] initWithCondition:1];
[self sendMessage:message callback:^(NSString *result) {
resultMessage = result;
[lock lock];
[lock unlockWithCondition:0];
}];
[lock lockWhenCondition:0];
[lock unlock];
return resultMessage;
}
// 互斥鎖
- (NSString *)sendMessageMutex:(NSString *)message {
__block NSString *resultMessage;
pthread_mutex_t pMutex = PTHREAD_MUTEX_INITIALIZER;
__block pthread_cond_t pCond = PTHREAD_COND_INITIALIZER;
pthread_mutex_lock(&pMutex);
[self sendMessage:message callback:^(NSString *result) {
resultMessage = result;
pthread_cond_signal(&pCond);
}];
pthread_cond_wait(&pCond, &pMutex);
pthread_mutex_destroy(&pMutex);
return resultMessage;
}
// 自旋鎖
- (NSString *)sendMessageSpin:(NSString *)message {
__block NSString *resultMessage;
__block OSSpinLock oslock = OS_SPINLOCK_INIT;
OSSpinLockLock(&oslock);
[self sendMessage:message callback:^(NSString *result) {
resultMessage = result;
OSSpinLockUnlock(&oslock);
}];
OSSpinLockLock(&oslock);
return resultMessage;
}