在日常开发中咱们总是会和网络打交道,从服务端拿数据渲染UI、上传数据到服务器、登陆等,那么就会遇到一些问题。eg:当用户登陆结束后才获取数据渲染UI或许是多个网络恳求从服务端拿到多个数据后,才进行下一步的操作,那么对网络恳求之间次序的控制是十分重要的,本文对这两种情况进行总结,如有不足之处,请多多指教。同时本文只提供了部分截图,其他运转作用可自行测验。

Swift版:/post/710075…

注:其间GlobalQueue、MainQueue分别代表大局并发行列和主行列

#define GlobalQueue dispatch_get_global_queue(0, 0)

#define MainQueue dispatch_get_main_queue()

#define CurrentThread [NSThread currentThread]

本文涉及到的第三方库Bolts后续会专门出一篇文章进行解说

情形一:多个网络恳求履行(无序)完后,在履行其他操作

1. 行列组group + notify

思路:group其实便是办理指定queue中使命的。在这儿经过调用dispatch_group_notify办法,等候group中办理queue的使命履行结束后,会在该group指定的行列中履行block内部的代码。其间进入dispatch_group_async函数时group会对使命数+1,离开dispatch_group_async时group会对当时的使命数-1.当group中的使命数为0时会触发dispatch_group_notify中的block

- (void)multipleRequest_NoOrder_after_executeOtherTask_byGroupNotify {
  dispatch_group_t group = dispatch_group_create();
  // dispatch_group_async会主动帮我办理group中使命的数量
  dispatch_group_async(group, GlobalQueue, ^{
    sleep(1);
    NSLog(@"网络恳求1,线程:%@",CurrentThread);
  });
  dispatch_group_async(group, GlobalQueue, ^{
    sleep(1);
    NSLog(@"网络恳求2,线程:%@",CurrentThread);
  });
  dispatch_group_async(group, GlobalQueue, ^{
    sleep(1);
    NSLog(@"网络恳求3,线程:%@",CurrentThread);
  });
  dispatch_group_async(group, GlobalQueue, ^{
    sleep(1);
    NSLog(@"网络恳求4,线程:%@",CurrentThread);
  });
  //当group中的使命履行完后,会调用
  dispatch_group_notify(group, MainQueue, ^{
    NSLog(@"更新UI,线程:%@",CurrentThread);
  });
}

第一次运转:

ios多个网络请求之间的处理(OC版)
第2次运转:
ios多个网络请求之间的处理(OC版)

2. NSOperation + NSOperationQueue

思路:经过NSBlockOperation创立多个使命,将使命增加到NSBlockOperationQueue中.经过调用addBarrierBlock办法,会比及将使命增加到NSBlockOperationQueue中一切的使命履行结束,才会履行barrierBlock中的代码.同时会阻塞当时NSBlockOperationQueue中比barrierBlock后增加的其他使命,当barrierBlock履行完后才后持续履行queue中比barrierBlock后增加的block使命。和dispatch_barrier_async作用类似

- (void)multipleRequest_NoOrder_after_executeOtherTask_byOperation {
  NSBlockOperation *block1 = [NSBlockOperation blockOperationWithBlock:^{
    sleep(1);
    NSLog(@"网络恳求1,线程:%@",CurrentThread);
  }];
  NSBlockOperation *block2 = [NSBlockOperation blockOperationWithBlock:^{
    sleep(1);
    NSLog(@"网络恳求2,线程:%@",CurrentThread);
  }];
  NSBlockOperation *block3 = [NSBlockOperation blockOperationWithBlock:^{
    sleep(1);
    NSLog(@"网络恳求3,线程:%@",CurrentThread);
  }];
  NSBlockOperation *block4 = [NSBlockOperation blockOperationWithBlock:^{
    sleep(1);
    NSLog(@"网络恳求4,线程:%@",CurrentThread);
  }];
  NSOperationQueue *queue = [[NSOperationQueue alloc] init];
  // 设置最大并发数
  queue.maxConcurrentOperationCount = [NSProcessInfo processInfo].activeProcessorCount;
  // waitUntilFinished:是否等候queue中的使命履行完后,才履行后面的代码。会阻塞当时线程
  [queue addOperations:@[block1,block2,block3,block4] waitUntilFinished:NO];
  // 当queue中的一切使命履行完后,会调用
 [queue addBarrierBlock:^{
 [[NSOperationQueue mainQueue] addOperationWithBlock:^{
      NSLog(@"更新UI,线程:%@",CurrentThread);
   }];
 }];
  NSLog(@"123");
 [queue addOperation:[NSBlockOperation blockOperationWithBlock:^{
   NSLog(@"BarrierBlock履行结束,我才干履行,线程:%@",CurrentThread);
  }]];
  NSLog(@"456");
}

第一次运转:

ios多个网络请求之间的处理(OC版)

第2次运转:

ios多个网络请求之间的处理(OC版)

假如你对多线程满足了解,咱们不必运转都能知道打印成果: 由于operation中的使命是在子线程中履行所以不阻塞当时的主线程.所以每一次输出次序都是123、456. 由于4个网络恳求在子线程中履行,次序不是固定的.所以接下来应该随机打印这4个网络恳求. 然后打印‘更新UI’.最终打印’BarrierBlock履行结束,我才干履行’.

3. 信号量dispatch_semaphore_t

思路:信号量dispatch_semaphore_t其实底层是经过加锁操作实现的。类似于操作系统中的PV操作,感兴趣的读者能够阅读:https://blog.csdn.net/daijin888888/article/details/51423678

- (void)multipleRequest_NoOrder_after_executeOtherTask_bySemaphore {
    // 传入的值有必要大于等于0
  dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
  dispatch_async(GlobalQueue, ^{
    sleep(1);
    NSLog(@"网络恳求1,线程:%@",CurrentThread);
    dispatch_semaphore_signal(semaphore);
  });
  dispatch_async(GlobalQueue, ^{
    sleep(1);
    NSLog(@"网络恳求2,线程:%@",CurrentThread);
    dispatch_semaphore_signal(semaphore);
  });
  dispatch_async(GlobalQueue, ^{
    sleep(1);
    NSLog(@"网络恳求3,线程:%@",CurrentThread);
    dispatch_semaphore_signal(semaphore);
  });
  dispatch_async(GlobalQueue, ^{
    sleep(1);
    NSLog(@"网络恳求4,线程:%@",CurrentThread);
    dispatch_semaphore_signal(semaphore);
  });
  dispatch_async(YHMainQueue, ^{
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"更新UI,线程:%@",CurrentThread);
  });
}

第一次运转:

ios多个网络请求之间的处理(OC版)
第2次运转:

ios多个网络请求之间的处理(OC版)
“`

4. 栅门函数barrier()

思路:栅门函数其实和NSOperationQueue的addBarrierBlock办法相似,都是等候行列中的使命履行结束后,才履行barrierBlock中的代码.同时会阻塞当时queue中其他比barrierBlock后增加的block使命。 可是需求注意说到的坑点。

- (void)multipleRequest_NoOrder_after_executeOtherTask_byBarrier {
  dispatch_queue_t queue = dispatch_queue_create("ConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
  dispatch_async(queue, ^{
    sleep(1);
    NSLog(@"网络恳求1,线程:%@",CurrentThread);
  });
  dispatch_async(queue, ^{
    sleep(1);
    NSLog(@"网络恳求2,线程:%@",CurrentThread);
  });
  dispatch_async(queue, ^{
    sleep(1);
    NSLog(@"网络恳求3,线程:%@",CurrentThread);
  });
  dispatch_async(queue, ^{
    sleep(1);
    NSLog(@"网络恳求4,线程:%@",CurrentThread);
  });
  // 坑点:不能用大局并发行列,栅门函数会失效而且栅门函数只能用于自定义创立的并发行列,假如传递dispatch_get_global_queue或许串行行列则dispatch_barrier_async等价于dispatch_async
  dispatch_barrier_async(queue, ^{
    dispatch_async(MainQueue, ^{
      NSLog(@"更新UI,线程:%@",CurrentThread);
     });
  });
}

第一次运转:

ios多个网络请求之间的处理(OC版)
第2次运转:
ios多个网络请求之间的处理(OC版)

5. 行列组group + enter + leave + notify.

由于dispatch_async是异步+大局并发行列,所以开启新的线程履行使命。同时异步不会阻塞当时线程,所以循环中的block使命一旦提交就遍历后续元素。dispatch_group_enter和dispatch_group_leave分别代表group的中使命数-1和+1.当使命数等于0时就会走到dispatch_group_notify办法

- (void)multiRequest_NoOrder_after_executeOtherTask_byGroup {
    NSArray <NSString *> *netWorkTasks = @[@"网络恳求1",@"网络恳求2",@"网络恳求3",@"网络恳求4"];
  dispatch_group_t group = dispatch_group_create();
  NSMutableArray *result = @[].mutableCopy;
  [netWorkTasks enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        // 使命数+1
    dispatch_group_enter(group);
    /// 网络恳求是异步的
    dispatch_async(GlobalQueue, ^{
      /// 模仿耗时操作
      sleep(1);
            NSLog(@"%@,线程:%@",obj,CurrentThread);
      /// 模仿内部处理完数据后,回调主线程.
      dispatch_async(MainQueue, ^{
                // 使命数-1
        dispatch_group_leave(group);
      });
    });
  }];
    // 使命数 = 0时
  dispatch_group_notify(group, MainQueue, ^{
        NSLog(@"更新UI,线程:%@",CurrentThread);
  });
}

运转截图: 第一次运转:

ios多个网络请求之间的处理(OC版)
第2次运转:
ios多个网络请求之间的处理(OC版)

6.凭借三方库Bolts

这儿每一个BFTask代表一个网络恳求.当taskForCompletionOfAllTasks中的一切BFTask履行结束后会调用continueWithBlock中的代码.该库后续会专门出一篇帖子解说.

- (void)multipleRequest_NoOrder_after_executeOtherTask_byBFTask {
  NSArray <BFTask <NSString *> *> *tasks = @[
    [self getStringBFTaskByNetRequestWithNumber:@1],
    [self getStringBFTaskByNetRequestWithNumber:@2],
    [self getStringBFTaskByNetRequestWithNumber:@3]
  ];
  [[BFTask taskForCompletionOfAllTasks:tasks] continueWithBlock:^id _Nullable(BFTask * _Nonnull t) {
        NSLog(@"网络恳求的成果: %@,线程:%@",[tasks valueForKey:@"result"],CurrentThread);
    return nil;
  }];
}
- (BFTask <NSString *> *)getBFTaskByNetRequestWithNumber:(NSNumber *)number {
  BFTaskCompletionSource *task = [BFTaskCompletionSource taskCompletionSource];
  dispatch_async(GlobalQueue, ^{
    sleep(1);
    NSLog(@"网络恳求 %zd,线程:%@",number.integerValue,CurrentThread);
    dispatch_async(MainQueue, ^{
      [task trySetResult:[NSString stringWithFormat:@"网络恳求 数据%zd",number.integerValue]];
    });
  });
  return task.task;
}

第一次运转:

ios多个网络请求之间的处理(OC版)

第2次运转:

ios多个网络请求之间的处理(OC版)

情形二:多个网络恳求履行(有序)完后,在履行其他操作 (线程同步方案)

1.经过异步+串行行列

原理:异步开启子线程,可是由所以串行行列,使命的履行依照FIFO的原则,那么先进入行列的网络恳求使命,会被子线程优先从行列中取出来履行。最终在网络恳求4完成后,在主线程改写UI。

- (void)multipleRequest_InOrder_after_executeOtherTask_byAsyncSerialQueue {
  dispatch_queue_t asyncSerialQueue = dispatch_queue_create("AsyncSerialQueue", DISPATCH_QUEUE_SERIAL);
  dispatch_async(asyncSerialQueue, ^{
    sleep(1);
    NSLog(@"网络恳求1,线程:%@",CurrentThread);
  });
  dispatch_async(asyncSerialQueue, ^{
    sleep(1);
    NSLog(@"网络恳求2,线程:%@",CurrentThread);
  });
  dispatch_async(asyncSerialQueue, ^{
    sleep(1);
    NSLog(@"网络恳求3,线程:%@",CurrentThread);
  });
  dispatch_async(asyncSerialQueue, ^{
    sleep(1);
    NSLog(@"网络恳求4,线程:%@",CurrentThread);
    dispatch_async(MainQueue, ^{
      NSLog(@"更新UI,线程:%@",CurrentThread);
    });
  });
}

运转截图:

ios多个网络请求之间的处理(OC版)

2.NSOperation + NSOperationQueue(增加使命之间的依赖联系)
- (void)multipleRequest_InOrder_after_executeOtherTask_byOperation {
  NSBlockOperation *block1 = [NSBlockOperation blockOperationWithBlock:^{
    sleep(1);
    NSLog(@"网络恳求1,线程:%@",CurrentThread);
  }];
  NSBlockOperation *block2 = [NSBlockOperation blockOperationWithBlock:^{
    sleep(1);
    NSLog(@"网络恳求2,线程:%@",CurrentThread);
  }];
  NSBlockOperation *block3 = [NSBlockOperation blockOperationWithBlock:^{
    sleep(1);
    NSLog(@"网络恳求3,线程:%@",CurrentThread);
  }];
  NSBlockOperation *block4 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"网络恳求4,线程:%@",CurrentThread);
    sleep(1);
  }];
  [block4 addDependency:block3];
  [block3 addDependency:block2];
  [block2 addDependency:block1];
  NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
  [operationQueue addOperations:@[block1,block2,block3,block4] waitUntilFinished:NO];
  //会比及queue中的使命履行完后才会调用
  [operationQueue addBarrierBlock:^{
    dispatch_async(dispatch_get_main_queue(), ^{
      NSLog(@"更新UI,线程:%@",CurrentThread);
    });
  }];
}

运转成果:

ios多个网络请求之间的处理(OC版)

3.条件NSConditionLock
- (void)multipleRequest_InOrder_after_executeOtherTask_byConditionLock {
  // 初始化条件为1
  NSConditionLock *conditionLock = [[NSConditionLock alloc] initWithCondition:1];
  /**
  *lockWhenCondition(加锁):当特定条件值时,才会加锁,不然线程休眠
  *unlockWithCondition(解锁):解锁并将条件值设置为传入的值
  */
  dispatch_async(GlobalQueue, ^{
    [conditionLock lockWhenCondition:1];
    NSLog(@"网络恳求1,线程:%@",CurrentThread);
    sleep(1);
    [conditionLock unlockWithCondition:2];
  });
  dispatch_async(GlobalQueue, ^{
    [conditionLock lockWhenCondition:2];
    NSLog(@"网络恳求2,线程:%@",CurrentThread);
    sleep(1);
    [conditionLock unlockWithCondition:3];
  });
  dispatch_async(GlobalQueue, ^{
    [conditionLock lockWhenCondition:3];
    NSLog(@"网络恳求3,线程:%@",CurrentThread);
    sleep(1);
    [conditionLock unlockWithCondition:4];
  });
  dispatch_async(GlobalQueue, ^{
    [conditionLock lockWhenCondition:4];
    NSLog(@"网络恳求4,线程:%@",CurrentThread);
    sleep(1);
    [conditionLock unlockWithCondition:1];
    dispatch_async(MainQueue, ^{
      NSLog(@"更新UI,线程:%@",CurrentThread);
    });
  });
}

运转截图:

ios多个网络请求之间的处理(OC版)

4.经过嵌套闭包(回调闭包)
- (void)multipleRequest_InOrder_after_executeOtherTask_byNestedBlock {
  // 这儿能够依据自己的需求来,eg:咱们能够依据网路恳求1获取的数据,做为网络恳求2的参数等等
  NSNumber *paramas1 = @10;
  [self requestWithNumber:@1 paramas:paramas1 completion:^(NSNumber *response) {
    NSNumber *paramas2 = response;
    [self requestWithNumber:@2 paramas:paramas2 completion:^(NSNumber *response) {
      NSNumber *paramas3 = response;
      [self requestWithNumber:@3 paramas:paramas3 completion:^(NSNumber *response) {
        dispatch_async(MainQueue, ^{
          NSLog(@"response = %@",response);
          NSLog(@"改写UI");
        });
      }];
    }];
  }];
}
- (void)requestWithNumber:(NSNumber *)number
         paramas:(NSNumber *)paramas
       completion:(void (^)(NSNumber *response))completion {
  dispatch_async(GlobalQueue, ^{
    sleep(1);
    NSLog(@"网络恳求 %zd,参数:'%@',线程:%@",number.integerValue,paramas,CurrentThread);
    dispatch_async(MainQueue, ^{
      completion(@(paramas.integerValue * 2));
    });
  });
}

运转成果:

ios多个网络请求之间的处理(OC版)

5.设置方针行列

思路:经过给行列设置方针行列,让原本在不同行列中异步/同步履行的使命在同一个串行行列中次序履行.那么方针行列明显只能是串行行列

- (void)multipleRequest_InOrder_after_executeOtherTask_bySetTargetQueue {
  // 方针行列是串行
  dispatch_queue_t targetQueue = dispatch_queue_create("targetQueue", DISPATCH_QUEUE_SERIAL);
  // 这儿能够是串行或并发行列
  dispatch_queue_t queue1 = dispatch_queue_create("test.1", DISPATCH_QUEUE_CONCURRENT);
  dispatch_queue_t queue2 = dispatch_queue_create("test.2", DISPATCH_QUEUE_SERIAL);
  dispatch_queue_t queue3 = dispatch_queue_create("test.3", DISPATCH_QUEUE_CONCURRENT);
  dispatch_queue_t queue4 = dispatch_queue_create("test.4", DISPATCH_QUEUE_CONCURRENT);
  // 设置方针行列
  dispatch_set_target_queue(queue1, targetQueue);
  dispatch_set_target_queue(queue2, targetQueue);
  dispatch_set_target_queue(queue3, targetQueue);
  dispatch_set_target_queue(queue4, targetQueue);
  dispatch_async(queue1, ^{
    sleep(1);
    NSLog(@"网络恳求1,线程:%@",CurrentThread);
  });
  dispatch_async(queue2, ^{
    sleep(1);
    NSLog(@"网络恳求2,线程:%@",CurrentThread);
  });
  dispatch_async(queue3, ^{
    sleep(1);
    NSLog(@"网络恳求3,线程:%@",CurrentThread);
  });
  dispatch_async(queue4, ^{
    dispatch_async(dispatch_get_main_queue(), ^{
      NSLog(@"更新UI,线程:%@",CurrentThread);
    });
  });
}

运转成果:

ios多个网络请求之间的处理(OC版)

6.凭借三方库Bolts
- (BFTask <NSNumber *> *)getNumberBFTaskByNetRequestWithNumber:(NSNumber *)number paramas:(NSNumber *)paramas {
  BFTaskCompletionSource *task = [BFTaskCompletionSource taskCompletionSource];
  dispatch_async(GlobalQueue, ^{
    sleep(1);
    NSLog(@"网络恳求 %zd,线程:%@",number.integerValue,CurrentThread);
    dispatch_async(MainQueue, ^{
      [task trySetResult:@(paramas.integerValue * 2)];
    });
  });
  return task.task;
}
- (void)multipleRequest_InOrder_after_executeOtherTask_byBFTask {
  NSNumber *paramas1 = @10;
  // 这儿能够依据自己的需求来,eg:咱们能够依据网路恳求1获取的数据,做为网络恳求2的参数等等
  [[[[self getNumberBFTaskByNetRequestWithNumber:@1 paramas:paramas1] continueWithBlock:^id _Nullable(BFTask<NSNumber *> * _Nonnull t) {
    NSLog(@"网络恳求1成果:%@,线程:%@",t.result,CurrentThread);
    NSNumber *paramas2 = t.result;
    return [self getNumberBFTaskByNetRequestWithNumber:@2 paramas:paramas2];
  }] continueWithBlock:^id _Nullable(BFTask * _Nonnull t) {
    NSLog(@"网络恳求2成果:%@,线程:%@",t.result,CurrentThread);
    NSNumber *paramas3 = t.result;
    return [self getNumberBFTaskByNetRequestWithNumber:@3 paramas:paramas3];
  }] continueWithBlock:^id _Nullable(BFTask * _Nonnull t) {
    NSLog(@"网络恳求3成果:%@,线程:%@",t.result,CurrentThread);
    return [BFTask taskWithResult:nil];
  }];
}

运转成果:

ios多个网络请求之间的处理(OC版)