应用的实例背景交代:
SDK提供给APP层一个接口。
- 假设app需要调用一个sdk中的接口,获取执行一个操作的结果。比如
-(BOOL)showMeResult;
- 但是实际情况是,我SDK中对这个执行结果的响应是分散在两个不同的函数中的。好比我们交易中的二次授权,
-
A函数
中发起二次授权,需要等待一段时间对IC卡进行写卡操作
-(BOOL)FunctionA { // ------ 伪代码,仅为说明用 return [self startSecondIssuance]; }
-
B函数
作为监听,写卡完成后才能获取到二次授权的结果
if (self.isSuccess) {
NSLog(@"二次授权成功,在这里才能通知'--showMeResult--'的结果");
} else
NSLog(@"二次授权失败,在这里才能通知'--showMeResult--'的结果");
} -
问题综述
- 等于是APP调用
-(BOOL)showMeResult
-> 调用SDK的-(BOOL)FunctionA
-> 发起监听,至回调到-(void)FunctionB
中才能取到 APP 调用函数的执行结果 - APP调用时候,代码是一行一行执行的,而我在
-(BOOL)FunctionA
中无法知悉-(void)FunctionB
中的结果何时才能完成。这里就要用到线程阻塞。
解决方案
前提:
APP调用-(BOOL)showMeResult
的时候,必须要另开一个线程来做等待,因为不论是下面的方案一,还是方案二,都会阻塞线程。阻塞线程的操作,不能在主线程做,否则造成主线程死锁。
{ // 创建队列开辟线程
dispatch_queue_t globalQueue = dispatch_queue_create("serialQ", DISPATCH_QUEUE_SERIAL);
// 执行
dispatch_async(globalQueue, ^{
NSLog(@"另开线程,开始工作!");
BOOL rslt = [self showMeResult];
});
}
方案一:使用while循环
- 优点:简单,除了上面说到的APP层调用的时候要用到GCD线程以外,在SDK层不需要再写任何的线程开辟。
- 缺点:while循环会让CPU一直处于读状态,没有进入休眠,会消耗大量的资源。
// ------------- app 层调用 -------------
-(IBAction)WaitForValue:(UIButton *)sender {
dispatch_queue_t globalQueue = dispatch_queue_create("serialQ", DISPATCH_QUEUE_SERIAL);
dispatch_async(globalQueue, ^{
// task
NSLog(@"另开线程,开始工作!");
BOOL rslt = [self WaitForValue_SDK];
});
}
// ------------- SDK层 -------------
-(BOOL)WaitForValue_SDK { //
NSLog(@"wait finished...");
while (!self.Info) {
NSLog(@"+++++ keep waiting... +++++++");
}
NSLog(@"出循环,有返回值!");
return YES;
}
//在另外一个地方对self.Info赋值
-(IBAction)Resume:(UIButton *)sender {
NSLog(@"1.请继续");
self.Info = @"resume";
}
方案二:用NSCondition做线程锁,线程等待结果来完成
- 优点:通知线程休眠,等待值完成
- 缺点:代码比较多(其实也不算什么缺点)
- NSCondition 介绍
// ------------- app 层调用 -------------
- (IBAction)WaitForValue_UsingThread:(UIButton *)sender {
dispatch_queue_t globalQueue = dispatch_queue_create("serialQ", DISPATCH_QUEUE_SERIAL);
dispatch_async(globalQueue, ^{
// task
NSLog(@"另开线程,开始工作!");
[self WaitForValue_Thread];
});
}
// ------------- SDK 层调用 -------------
-(BOOL)WaitForValue_Thread {
self.condition = [[NSCondition alloc] init];
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self.condition lock];
if(!self.Thread_Info) {
NSLog(@"thread 0.开始等待赋值..");
[self.condition wait];
}
NSLog(@"thread 2.恢复了…………");
NSLog(@"thread 3.Thread_Info = %@",self.Thread_Info);
[self.condition unlock];
});
NSLog(@"wait finished...");
NSLog(@"4.出循环,有返回值!");
return YES;
}
- (IBAction)Resume_Thread:(UIButton *)sender {
// dispatch_queue_t Q = dispatch_queue_create("serialQ", DISPATCH_QUEUE_SERIAL);
//
// dispatch_async(Q, ^{
// [self.condition lock];
// NSLog(@"1.请继续");
// self.Info = @"resume";
// [self.condition signal];
// [self.condition unlock];
// });
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self.condition lock];
NSLog(@"thread 1.请继续");
self.Thread_Info = @"resume";
[self.condition signal];
[self.condition unlock];
});
}
日志打印结果
2016-06-05 20:33:41.602 ThreadCtrl[19015:625341] 另开线程,开始工作!
2016-06-05 20:33:41.603 ThreadCtrl[19015:625341] thread 0.开始等待赋值..
2016-06-05 20:33:43.859 ThreadCtrl[19015:625339] thread 1.请继续
2016-06-05 20:33:43.860 ThreadCtrl[19015:625341] thread 2.恢复了…………
2016-06-05 20:33:43.860 ThreadCtrl[19015:625341] thread 3.Thread_Info = resume
2016-06-05 20:33:43.860 ThreadCtrl[19015:625341] wait finished...
2016-06-05 20:33:43.861 ThreadCtrl[19015:625341] 4.出循环,有返回值!
SDK 层调用GCD的说明
- 线程等待部分记得 必须用
dispatch_sync
同步方式来做,否则会先执行返回值的部分
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self.condition lock];
// to do task...
[self.condition wait];
[self.condition unlock];
});
NSLog(@"wait finished...");
return YES;
- 上面的
wait
和resume
中使用到的GCD队列可以是同一个队列,也可以是不同的队列,似乎并不影响结果。也就是下面注释和没注释的部分结果是一样的
// dispatch_queue_t Q = dispatch_queue_create("serialQ", DISPATCH_QUEUE_SERIAL);
// dispatch_async(Q, ^{
// [self.condition lock];
// [self.condition signal];
// NSLog(@"1.请继续");
// self.Info = @"resume";
// [self.condition unlock];
// });
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self.condition lock];
[self.condition signal];
NSLog(@"thread 1.请继续");
self.Thread_Info = @"resume";
[self.condition unlock];
});
使用 GCD
GCD 中提供了一个信号发送/接收的方法,类似于上面提到的进程锁,可以将异步转化为同步。
下面是示例代码
对外暴露的是一个等待返回 BOOL
类型的结果。
// 等到同步执行的结果
- (BOOL)waiForAsyncExecution {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);//创建信号量
__block BOOL result = NO;
[[SomeNetworkClass sharedInstance] getAsyncResult:^(NSString *unit) {
if (unit) {
[self doSomething];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[SomeClass updateUI];// 更新 UI部分在主线程中执行,所以又放到了一个dispatch_async 中
dispatch_semaphore_signal(semaphore);//发送信号量
});
} else {
dispatch_semaphore_signal(semaphore);//发送信号量
}
}];
dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);//同步等待信号量
return result;
}