美文网首页
iOS多线程 接口同步

iOS多线程 接口同步

作者: 惊蛰_e3ce | 来源:发表于2019-11-23 12:59 被阅读0次

场景:一个页面有多个接口 需要等多个接口全都请求完成之后再刷新UI reloadData.

1:串行请求,接口嵌套 耗时久,有点low。
2:dispatch_group
用dispatch_after模拟接口耗时。
方式1: dispatch_group_notify
方式2: dispatch_group_wait(会阻塞当前线程不要放主线程用)
方式3:用信号量堵塞请求任务 请求完成才增加信号量完成任务
如果直接如下:

dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_group_async(group, queue, ^{
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                NSLog(@"????2");
        });
    });
    
    dispatch_group_async(group, queue, ^{
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                NSLog(@"????1");
        });
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"任务已完成");
    });

打印结果

2019-11-22 15:26:27.846570+0800 11[17701:770534] 任务已完成
2019-11-22 15:26:30.827562+0800 11[17701:770534] ????2
2019-11-22 15:26:37.827784+0800 11[17701:770534] ????1

因为dispatch_after是异步瞬间完成 需要配合leavegroup entergroup来用
更改如下:

方式1:
dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_group_enter(group);
    dispatch_group_async(group, queue, ^{
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                NSLog(@"????2");
            dispatch_group_leave(group);
        });
    });
    
    dispatch_group_enter(group);
    dispatch_group_async(group, queue, ^{
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                NSLog(@"????1");
            dispatch_group_leave(group);
        });
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"任务已完成");
    });

log如下

2019-11-22 15:37:06.231599+0800 11[17731:774655] ????2
2019-11-22 15:37:13.231893+0800 11[17731:774655] ????1
2019-11-22 15:37:13.232329+0800 11[17731:774655] 任务已完成
方式2:
    [self performSelectorInBackground:@selector(test2) withObject:nil];
- (void)test2 {
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_group_enter(group);
    dispatch_group_async(group, queue, ^{
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                NSLog(@"????2");
            dispatch_group_leave(group);
        });
    });
    
    dispatch_group_enter(group);
    dispatch_group_async(group, queue, ^{
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                NSLog(@"????1");
            dispatch_group_leave(group);
        });
    });
    
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    NSLog(@"任务已完成");

}
方式3:
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
       dispatch_group_async(group, queue, ^{
              [self request1];
          });
    
        dispatch_group_async(group, queue, ^{
                   [self request2];
        });
          dispatch_group_notify(group, dispatch_get_main_queue(), ^{
              NSLog(@"任务已完成");
          });
}
- (void)request1{
    dispatch_semaphore_t  semaphore = dispatch_semaphore_create(0);
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"请求1完成");
        dispatch_semaphore_signal(semaphore);
    });
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}
- (void)request2{
    dispatch_semaphore_t  semaphore = dispatch_semaphore_create(0);
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"请求2完成");
        dispatch_semaphore_signal(semaphore);
    });
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}

场景:上百张图片每10张10张上传

信号量方式  用信号量堵塞异步数据请求直到请求完成
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    //信号量初始化为10
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
        
    //同时执行100个任务
     for (int i = 0; i < 100; i++)
     {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                
        //可用信号量-1
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
                
        NSLog(@"任务%d执行",i+1);
            [self request1];
        //可用信号量+1
        dispatch_semaphore_signal(semaphore);
                
        });
     }
}
- (void)request2{
    dispatch_semaphore_t  semaphore = dispatch_semaphore_create(0);
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(30 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"请求2完成");
        dispatch_semaphore_signal(semaphore);
    });
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}    

场景:A请求依赖B请求 其中B是异步操作 用addDependency来实现 (这个是练习理解 自定义Operation 可以配合KVO在合适时机调用isFinished )

同样可以用来做AB接口请求同步 用GCD想取消的话不如这个方便

参考自:https://www.jianshu.com/p/e1b08b8bd5af
作者:Baoger
代码示例如下

.h
自定义operation
@class CustomOperation;


/// 使用delegate在外面完成对应的操作
@protocol CustomOperationDelegate <NSObject>

-(void)startOperation:(CustomOperation *)operation;

@end
@property (nonatomic, copy, readonly) NSString *taskName;

@property(nonatomic, weak) id <CustomOperationDelegate>delegate;

- (instancetype)initWithTaskName:(NSString *)taskName;
/// 操作完成时候外部调用改变状态
-(void)completeOperation;

.m
#import "CustomOperation.h"

@interface CustomOperation ()

/// 是否正在进行
@property (nonatomic, assign) BOOL bees_executing;

/// 是否完成
@property (nonatomic, assign) BOOL bees_finished;

@end

@implementation CustomOperation

-(instancetype)initWithTaskName:(NSString *)taskName {
  if (self = [super init]) {
    _bees_executing = NO;
    _bees_finished = NO;
    _taskName = taskName;
//    NSLog(@"%@ CustomOperation alloc %@", _taskName,self);
  }
  return self;
}

- (void)dealloc {
    NSLog(@"%@ dealloc %@", self.taskName,self);
}


- (void)start {
  if (self.isCancelled) {
    // 若当前操作为取消,则结束操作,且要修改isExecuting和isFinished的值,通过kvo的方式告诉对应的监听者其值的改变
    NSLog(@"%@ cancel %@", self.taskName,self);
    [self completeOperation];
  } else {
    // 正在执行操作
    self.bees_executing = YES;
    // 通过代理,在外部实现对应的异步操作
    if (self.delegate && [self.delegate respondsToSelector:@selector(startOperation:)]) {
      [self.delegate startOperation:self];
    }
  }
}

/// 结束当前操作,改变对应的状态
- (void)completeOperation {
  self.bees_executing = NO;
  self.bees_finished = YES;
}

// 一定要重写cancel方法,结束状态
- (void)cancel {
  [super cancel];
  // 取消后一定要调用完成,删除queue中的operation
  [self completeOperation];
}

#pragma mark - settter and getter
// setter 修改自己状态的同时,发送父类对应属性状态改变的kvo通知
- (void)setBees_executing:(BOOL)bees_executing {
  [self willChangeValueForKey:@"isExecuting"];
  _bees_executing = bees_executing;
  [self didChangeValueForKey:@"isExecuting"];
}

- (void)setBees_finished:(BOOL)bees_finished {
  [self willChangeValueForKey:@"isFinished"];
  _bees_finished = bees_finished;
  [self didChangeValueForKey:@"isFinished"];
}

// 父类返回自己维护的对应的状态
- (BOOL)isExecuting {
  return self.bees_executing;
}

- (BOOL)isFinished {
  return self.bees_finished;
}
- (BOOL)isAsynchronous {
    return true;
}
//调用
- (void)viewDidLoad {
    [super viewDidLoad];
    CustomOperation *uploadOperation = [[CustomOperation alloc]initWithTaskName:@"test"];
    uploadOperation.delegate = self;
    NSBlockOperation *block = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"33333");
    }];
    [block addDependency:uploadOperation];
    
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    queue.maxConcurrentOperationCount = 4;
    [queue addOperation:uploadOperation];
    [queue addOperation:block];
    return;
}
- (void)startOperation:(CustomOperation *)operation {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"第一个任务完毕");
                    [operation completeOperation];///这儿很关键 手动标识任务finished
    });
    return;
}
@end

进阶参考:1.概论

An operation object is a single-shot object—that is, it executes its task once
and cannot be used to execute it again. You typically execute operations by
adding them to an operation queue (an instance of
the [NSOperation<wbr>Queue](apple-reference-
documentation://hcOi0eohIY) class). An operation queue executes its
operations either directly, by running them on secondary threads, or
indirectly using the libdispatch library (also known as Grand Central
Dispatch). For more information about how queues execute operations,
see [NSOperation<wbr>Queue](apple-reference-
documentation://hcOi0eohIY).

If you do not want to use an operation queue, you can execute an operation
yourself by calling its start method directly from your code. Executing
operations manually does put more of a burden on your code, because
starting an operation that is not in the ready state triggers an exception.
The ready property reports on the operation’s readiness.

NSOperation可以通过start方法直接调用,但是更好的方法是将其放入NSOperationQueue中。
放入NSOperationQueue中的NSOperation会在合适的时机自动执行,如果没有依赖和并发数量限制,则会立刻执行(调用start方法),在NSOperationQueue中的NSOperation默认异步执行。
cancelAllOperations可以将NSOperationQueue中所有的NSOperation发送cancel的消息。

2.异步和同步

一个独立的Operation 默认是同步的,且在当前调用的线程中执行start方法。

When you call the start method of a synchronous operation directly from
your code, the operation executes immediately in the current thread.

asynchronous是一个控制异步同步的属性,YES可以让NSOperation异步执行,但是当Operation放入OperationQueue中时,asynchronous就失去意义了,Operation默认会变成异步。

  1. 非并发的NSOperation

非并发的NSOperation意味着是NSOperation中没有并发的操作,执行完成后这个NSOperation就失效了(状态变为isFinished)。 对于非并发的NSOperation,只需要覆盖main方法,在main中做操作,NSOperation会自动管理状态,比如在main开始之前将状态设置成isReading,在main方法调用完成将状态设置成isFinished。

For non-concurrent operations, you typically override only one method:
Into this method, you place the code needed to perform the given task. Of
course, you should also define a custom initialization method to make it
easier to create instances of your custom class. You might also want to
define getter and setter methods to access the data from the operation.
However, if you do define custom getter and setter methods, you must make
sure those methods can be called safely from multiple threads.

  1. 并发的NSOperation

对于有并发操作的NSOperation,需要手动管理状态。并且需要覆盖start方法。 NSOperation的状态

isReady

NSOperation准备好执行时候的状态,通常由于NSOperationQueue并发数的限制和NSOperation依赖的影响,NSOperation不总是立刻进入ready状态。

isExecuting

If you replace the start method of your operation object, you must also replace
the executing property and generate KVO notifications when the execution
state of your operation changes.
当你自定义并发operation的时候,executing的状态是需要自己通过KVO控制的。

-(void)setExecuting:(BOOL)executing{
[self willChangeValueForKey:@"isExecuting"];
_executing = executing;
[self didChangeValueForKey:@"isExecuting"];}
isFinished

isFinished 表明当前的operation完成,这样基于此operation依赖的operation才会执行,如果当operation被添加进queue中,当operation变为finished时,将会被自动移除queue,并且释放掉。 当你自定义并发operation的时候,finished的状态是需要自己通过KVO控制的。

-(void)setFinished:(BOOL)executing{
[self willChangeValueForKey:@"isFinished"];
_finished = finished;
[self didChangeValueForKey:@"isFinished"];
}
isCancelled

isCancelled可以帮助我们在必要的时候枝减掉某些操作,在并发任务中获取isCancelled的状态决定是否继续接下来的操作。
转自:https://m.meiwen.com.cn/subject/djwskftx.html

相关文章

  • 起底多线程同步锁(iOS)

    起底多线程同步锁(iOS) 起底多线程同步锁(iOS)

  • iOS多线程 接口同步

    场景:一个页面有多个接口 需要等多个接口全都请求完成之后再刷新UI reloadData. 1:串行请求,接口嵌套...

  • iOS 多线程基础

    转自:iOS 多线程基础 - 简书 多线程同步和异步的区别?IOS中如何实现多线程的同步? 异步:可以同时执行多条...

  • iOS复习之多线程

    关于iOS多线程,你看我就够了iOS多线程--彻底学会多线程使用dispatch_group来进行线程同步 iOS...

  • iOS中的锁

    起底多线程同步锁(iOS) OSSpinLock NSLock NSRecursiveLock 同步 NSCond...

  • OC--各种线程锁

    参考:正确使用多线程同步锁@synchronized()iOS中的锁iOS多线程安全详解iOS 常见知识点(三):...

  • iOS_2016最新版面试题(附答案)

    每天四道题,让精彩填满生活... 1、多线程同步和异步的区别。iOS如何实现多线程的同步? 答:同步就是指一个线程...

  • 线程锁

    探讨iOS开发中各种锁使用NSCondition实现多线程同步 NSCondition是线程同步, 阻塞线程。 取...

  • ios知识点(9)多线程

    iOS多线程GCD详解 使用GCD iOS多线程中,队列和执行的排列组合结果分析 存在一点小瑕疵,如果同步(syn...

  • iOS gcd看我就够了

    gcd是iOS中多线程的一种技术,下面我们来看看gcd的用法 同步&异步 同步 同步执行:字面意思,同步执行就是执...

网友评论

      本文标题:iOS多线程 接口同步

      本文链接:https://www.haomeiwen.com/subject/ykkqwctx.html