线程中的异步转同步 —— 应用实例

应用的实例背景交代:

SDK提供给APP层一个接口。

  • 假设app需要调用一个sdk中的接口,获取执行一个操作的结果。比如
    -(BOOL)showMeResult;
    
  • 但是实际情况是,我SDK中对这个执行结果的响应是分散在两个不同的函数中的。好比我们交易中的二次授权,
    • A函数中发起二次授权,需要等待一段时间对IC卡进行写卡操作
    -(BOOL)FunctionA {  // ------ 伪代码,仅为说明用
          return [self startSecondIssuance]; 
    }
    
    • B函数作为监听,写卡完成后才能获取到二次授权的结果
    -(void)FunctionB { // ------ 伪代码,仅为说明用
    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;
  • 上面的 waitresume中使用到的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;
}

你可能感兴趣的:(线程中的异步转同步 —— 应用实例)