iOS相关技术原理

OC底层原理 - 21 NSThread&GCD& NSOper

2021-04-20  本文已影响0人  卡布奇诺_95d2

本文的主要目的是介绍NSThreadGCDNSOperation常见的使用方式。

NSThread

NSthread是苹果官方提供面向对象的线程操作技术,是对thread的上层封装,比较偏向于底层。简单方便,可以直接操作线程对象,使用频率较少。它需要程序员自己管理线程的生命周期。

NSThread的使用如下

//方式1
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument API_AVAILABLE(macos(10.5), iOS(2.0), watchos(2.0), tvos(9.0));
//示例
NSThread* thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(doSomething:) object:@"方式1"];
[thread1 start];

//方式2
- (instancetype)initWithBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), iOS(10.0), watchos(3.0), tvos(10.0));
//示例
self.thread1 = [[NSThread alloc] initWithBlock:^{
    NSLog(@"%@",[NSThread currentThread]);
}];
[self.thread1 start];
//方式1
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;
//示例
[NSThread detachNewThreadSelector:@selector(doSomething:) toTarget:self withObject:@"方式1"];

//方式2
+ (void)detachNewThreadWithBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), iOS(10.0), watchos(3.0), tvos(10.0));
//示例
[NSThread detachNewThreadWithBlock:^{
    NSLog(@"%@", [NSThread currentThread]);
}];
//在主线程执行
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
//示例
[self performSelectorOnMainThread:@selector(doSomething:) withObject:@"方式3" waitUntilDone:YES modes:@[NSRunLoopCommonModes]];
[self performSelectorOnMainThread:@selector(doSomething:) withObject:@"方式3" waitUntilDone:YES];

//在指定线程执行
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array API_AVAILABLE(macos(10.5), iOS(2.0), watchos(2.0), tvos(9.0));
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait API_AVAILABLE(macos(10.5), iOS(2.0), watchos(2.0), tvos(9.0));
//示例
[self performSelectorOnMainThread:@selector(doSomething:) withObject:@"方式3" waitUntilDone:YES];
[self performSelector:@selector(doSomething:) onThread:[NSThread currentThread] withObject:@"方式3" waitUntilDone:YES modes:@[NSRunLoopCommonModes]];

//创建一个子线程执行
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg API_AVAILABLE(macos(10.5), iOS(2.0), watchos(2.0), tvos(9.0));
//示例
[self performSelectorInBackground:@selector(doSomething:) withObject:@"方式3"];

NSThread的属性

//表示线程是否正在执行
@property (readonly, getter=isExecuting) BOOL executing;
//表示线程是否执行完成
@property (readonly, getter=isFinished) BOOL finished;
//线程是否被取消
@property (readonly, getter=isCancelled) BOOL cancelled;
/** NSQualityOfService:
  NSQualityOfServiceUserInteractive:最高优先级,主要用于提供交互UI的操作,比如处理点击事件,绘制图像到屏幕上
  NSQualityOfServiceUserInitiated:次高优先级,主要用于执行需要立即返回的任务
  NSQualityOfServiceDefault:默认优先级,当没有设置优先级的时候,线程默认优先级
  NSQualityOfServiceUtility:普通优先级,主要用于不需要立即返回的任务
  NSQualityOfServiceBackground:后台优先级,用于完全不紧急的任务
*/
@property NSQualityOfService qualityOfService; // read-only after the thread is started

+ (double)threadPriority;
@property (nullable, copy) NSString *name;
@property NSUInteger stackSize;
//该线程中函数调用的虚拟地址的数组
@property (class, readonly, copy) NSArray<NSNumber *> *callStackReturnAddresses;
//线程调用函数的名字数字
@property (class, readonly, copy) NSArray<NSString *> *callStackSymbols;
//是否是主线程
@property (readonly) BOOL isMainThread;
@property (class, readonly) BOOL isMainThread; // reports whether current thread is main

//获取主线程
@property (class, readonly, strong) NSThread *mainThread;

//获取当前线程
@property (class, readonly, strong) NSThread *currentThread;

实例方法

- (void)start;

NSThread* thread = [[NSThread alloc] initWithTarget:self selector:@selector(doSomething:) object:@"方式1"];
[thread start];
- (void)cancel;

[thread cancel];
- (void)main;   // thread body method

[thread main];

类方法

+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
+ (void)exit;
+ (double)threadPriority;
+ (BOOL)setThreadPriority:(double)p;

GCD

dispatch_after函数

dispatch_after能让我们添加进队列的任务延时执行,该函数并不是在指定时间后执行处理,而只是在指定时间后追加处理到dispatch_queue

/*
参数1 when:任务在何时开始执行
参数2 queue:执行任务的队列
参数3 block:需要执行的任务
*/
void
dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block);
        
//示例
dispatch_queue_t queue = dispatch_queue_create("hqThread", DISPATCH_QUEUE_SERIAL);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2*NSEC_PER_SEC), queue, ^{
    NSLog(@"test dispatch_after");
});

dispatch_once函数

dispatch_once保证在App运行期间,block中的代码只执行一次。多用于创建单例的情况。

重点:dispatch_once是线程安全的。

/*
参数1 predicate:用于检查该代码块是否已经被调度的谓词,虽然它是一个长整形,但是它是作为BOOL使用,且当值超过
参数2 block:需要执行的任务
*/
void
dispatch_once(dispatch_once_t *predicate, DISPATCH_NOESCAPE dispatch_block_t block);

//示例
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    NSLog(@"创建单例");
});

dispatch_apply函数

该函数按指定的次数将指定的Block追加到指定的Dispatch Queue中,并等待全部处理执行结束。

注意:该函数是需要等待全部任务执行完成之后再继续函数后面的操作,效果与dispatch_sync类似,因此,在指定执行任务的线程时,需要防止死锁。

/*
参数1 iterations:指定任务执行的次数
参数2 queue:执行任务的线程
参数3 block:需要执行的任务,该block有个入参,表示当前是第几次执行
*/
void
dispatch_apply(size_t iterations,
        dispatch_queue_t DISPATCH_APPLY_QUEUE_ARG_NULLABILITY queue,
        DISPATCH_NOESCAPE void (^block)(size_t));

//示例
dispatch_apply(3, dispatch_queue_create("hqThread", DISPATCH_QUEUE_CONCURRENT), ^(size_t a) {
    NSLog(@"[%zu] current thread:%@", a, [NSThread currentThread]);
});

dispatch_group_t 任务组

它的主要用于监听管理任务组中任务完成情况。与dispatch_group_notify函数配合,可以在任务完成后做一些操作处理。

常用方法如下:

// 创建一个任务组
dispatch_group_t dispatch_group_create(void);

// 将任务添加到任务组里,并且异步执行
void dispatch_group_async(dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block);

// 表示接下来手动向任务组添加任务,即任务组中的任务数+1
void dispatch_group_enter(dispatch_group_t group);

// 任务组中任务数-1与dispatch_group_enter必须成对出现
void dispatch_group_leave(dispatch_group_t group);

// 等待之前任务执行完成后才继续执行
intptr_t dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout);

// 当任务组中任务完成,会出发出发此方法的block
void dispatch_group_notify(dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block);

接下来分别通过示例来说任务组的使用进行说明。

dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("hqThread", DISPATCH_QUEUE_SERIAL);
    
dispatch_group_async(group, queue, ^{
    [NSThread sleepForTimeInterval:2];
    NSLog(@"[任务1] %@", [NSThread currentThread]);
});
    
dispatch_group_enter(group);
dispatch_async(queue, ^{
    [NSThread sleepForTimeInterval:2];
    NSLog(@"[任务2] %@", [NSThread currentThread]);
});
    
dispatch_async(queue, ^{
    [NSThread sleepForTimeInterval:2];
    NSLog(@"[任务3] %@", [NSThread currentThread]);
    dispatch_group_leave(group);
});

dispatch_async(queue, ^{
    NSLog(@"wait start");
    dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC*6));
    NSLog(@"wait end");
});
    
dispatch_async(queue, ^{
    [NSThread sleepForTimeInterval:4];
    NSLog(@"[任务4] %@", [NSThread currentThread]);
});
    
dispatch_group_notify(group, queue, ^{
    NSLog(@"所有任务执行完成");
});
    
dispatch_async(queue, ^{
    [NSThread sleepForTimeInterval:4];
    NSLog(@"[任务5] %@", [NSThread currentThread]);
});

//当任务的队列为DISPATCH_QUEUE_SERIAL时,执行结果:
2021-04-16 16:27:21.101260+0800 Demo[87930:20756012] [任务1] <NSThread: 0x600003759880>{number = 7, name = (null)}
2021-04-16 16:27:23.106189+0800 Demo[87930:20756012] [任务2] <NSThread: 0x600003759880>{number = 7, name = (null)}
2021-04-16 16:27:25.111495+0800 Demo[87930:20756012] [任务3] <NSThread: 0x600003759880>{number = 7, name = (null)}
2021-04-16 16:27:25.111771+0800 Demo[87930:20756012] wait start
2021-04-16 16:27:31.112103+0800 Demo[87930:20756012] wait end
2021-04-16 16:27:35.116521+0800 Demo[87930:20756012] [任务4] <NSThread: 0x600003759880>{number = 7, name = (null)}
2021-04-16 16:27:39.119116+0800 Demo[87930:20756012] [任务5] <NSThread: 0x600003759880>{number = 7, name = (null)}
2021-04-16 16:27:39.119395+0800 Demo[87930:20756012] 所有任务执行完成

//当任务的队列为DISPATCH_QUEUE_CONCURRENT时,执行结果:
2021-04-16 16:48:48.220717+0800 Demo[88208:20774077] wait start
2021-04-16 16:48:50.223659+0800 Demo[88208:20774082] [任务1] <NSThread: 0x60000139d900>{number = 3, name = (null)}
2021-04-16 16:48:50.223659+0800 Demo[88208:20774080] [任务2] <NSThread: 0x6000013e10c0>{number = 6, name = (null)}
2021-04-16 16:48:50.223659+0800 Demo[88208:20774083] [任务3] <NSThread: 0x6000013e5f00>{number = 7, name = (null)}
2021-04-16 16:48:50.223970+0800 Demo[88208:20774077] wait end
2021-04-16 16:48:50.223979+0800 Demo[88208:20774082] 所有任务执行完成
2021-04-16 16:48:52.222427+0800 Demo[88208:20774078] [任务5] <NSThread: 0x6000013e06c0>{number = 9, name = (null)}
2021-04-16 16:48:52.222427+0800 Demo[88208:20774079] [任务4] <NSThread: 0x6000013e0a80>{number = 4, name = (null)}

总结:

  1. dispatch_group_async函数是自动将任务添加到任务组中。
  2. dispatch_group_enter函数是手动将任务添加到任务组中。
  3. 当通过dispatch_group_enter函数被调用后,dispatch_group_leave函数调用前,其间的所有任务都被添加到任务组中。
  4. 当任务所在的队列为串行队列时,会等待group关联队列中所有任务执行完成之后才会发出notify信号,调用dispatch_group_notify函数,但是,当group组中的任务执行完成之后,会执行dispatch_group_wait函数。

dispatch_barrier_sync & dispatch_barrier_async

栅栏函数,主要有两种使用场景:串行队列、并发队列

dispatch_queue_t queue = dispatch_queue_create("hqThread", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
    [NSThread sleepForTimeInterval:2];
    NSLog(@"[任务1] %@", [NSThread currentThread]);
});
    
dispatch_async(queue, ^{
    [NSThread sleepForTimeInterval:2];
    NSLog(@"[任务2] %@", [NSThread currentThread]);
});
    
dispatch_async(queue, ^{
    [NSThread sleepForTimeInterval:2];
    NSLog(@"[任务3] %@", [NSThread currentThread]);
});
    
dispatch_barrier_async(queue, ^{
    NSLog(@"------------栅栏任务------------%@", [NSThread currentThread]);
});
    
dispatch_async(queue, ^{
    NSLog(@"[任务4] %@", [NSThread currentThread]);
});
    
dispatch_async(queue, ^{
    NSLog(@"[任务5] %@", [NSThread currentThread]);
});

//执行结果
2021-04-19 09:46:18.453361+0800 Demo[13733:22451112] [任务1] <NSThread: 0x6000014b6c00>{number = 6, name = (null)}
2021-04-19 09:46:20.458536+0800 Demo[13733:22451112] [任务2] <NSThread: 0x6000014b6c00>{number = 6, name = (null)}
2021-04-19 09:46:22.463882+0800 Demo[13733:22451112] [任务3] <NSThread: 0x6000014b6c00>{number = 6, name = (null)}
2021-04-19 09:46:22.464409+0800 Demo[13733:22451112] ------------栅栏任务------------<NSThread: 0x6000014b6c00>{number = 6, name = (null)}
2021-04-19 09:46:22.464688+0800 Demo[13733:22451112] [任务4] <NSThread: 0x6000014b6c00>{number = 6, name = (null)}
2021-04-19 09:46:22.464940+0800 Demo[13733:22451112] [任务5] <NSThread: 0x6000014b6c00>{number = 6, name = (null)}
dispatch_queue_t queue = dispatch_queue_create("hqThread", DISPATCH_QUEUE_CONCURRENT);//DISPATCH_QUEUE_CONCURRENT
dispatch_async(queue, ^{
    [NSThread sleepForTimeInterval:2];
    NSLog(@"[任务1] %@", [NSThread currentThread]);
});
    
dispatch_async(queue, ^{
    [NSThread sleepForTimeInterval:2];
    NSLog(@"[任务2] %@", [NSThread currentThread]);
});
    
dispatch_async(queue, ^{
    [NSThread sleepForTimeInterval:2];
    NSLog(@"[任务3] %@", [NSThread currentThread]);
});
    
dispatch_barrier_async(queue, ^{
    NSLog(@"------------栅栏任务------------%@", [NSThread currentThread]);
});
    
dispatch_async(queue, ^{
    NSLog(@"[任务4] %@", [NSThread currentThread]);
});
    
dispatch_async(queue, ^{
    NSLog(@"[任务5] %@", [NSThread currentThread]);
});

//执行结果
2021-04-19 09:47:09.266069+0800 Demo[13753:22452474] [任务2] <NSThread: 0x60000192c940>{number = 5, name = (null)}
2021-04-19 09:47:09.266121+0800 Demo[13753:22452477] [任务1] <NSThread: 0x60000192c240>{number = 8, name = (null)}
2021-04-19 09:47:09.266191+0800 Demo[13753:22452478] [任务3] <NSThread: 0x6000019212c0>{number = 3, name = (null)}
2021-04-19 09:47:09.266493+0800 Demo[13753:22452478] ------------栅栏任务------------<NSThread: 0x6000019212c0>{number = 3, name = (null)}
2021-04-19 09:47:09.266699+0800 Demo[13753:22452478] [任务4] <NSThread: 0x6000019212c0>{number = 3, name = (null)}
2021-04-19 09:47:09.266762+0800 Demo[13753:22452474] [任务5] <NSThread: 0x60000192c940>{number = 5, name = (null)}

从上面代码和结果分析可知,dispatch_barrier_async函数的主要作用是等待队列中,位于dispatch_barrier_async函数前的任务完成之后,再执行dispatch_barrier_async函数后的任务。

在串行队列中,任务本身就是按顺序执行,因此,栅栏函数的作用不明显。而在并发队列中,使用栅栏函数可以很好的控制队列内任务执行的顺序。

重点:dispatch_barrier_asyncdispatch_barrier_sync
相同点:都是起到栅栏的作用,必须等待栅栏前面的任务执行完成之后再执行栅栏之后的任务。
不同点:dispatch_barrier_sync函数不仅会阻塞队列任务的执行,也会阻塞线程的执行,阻塞线程执行同dispatch_sync函数类似,也可能会引起死锁。所以dispatch_barrier_sync函数需要慎用。

栅栏函数中尽量使用自定义队列,因为使用全局队列是无效的,因为使用全局队列时由于对全局队列造成堵塞,可能致使系统其他调用全局队列的地方也堵塞从而导致崩溃。

dispatch_semaphore_t 信号量

信号量主要用作同步锁,用于控制GCD最大并发数。常见的函数有:

dispatch_semaphore_create():创建信号量dispatch_semaphore_t

dispatch_semaphore_wait():信号量减1.当信号量< 0时会阻塞当前线程,根据传入的等待时间决定接下来的操作——如果永久等待将等到信号(signal)才执行下去

dispatch_semaphore_signal():信号量加1.当信号量>= 0 会执行wait之后的代码.

示例代码如下:

dispatch_queue_t queue = dispatch_queue_create("hqThread", DISPATCH_QUEUE_CONCURRENT);
        
for (int i = 0; i < 10; i++) {
    dispatch_async(queue, ^{
        NSLog(@"当前 - %d, 线程 - %@", i, [NSThread currentThread]);
    });
}

//利用信号量来改写
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
for (int i = 0; i < 10; i++) {
    dispatch_async(queue, ^{
        NSLog(@"当前 - %d, 线程 - %@", i, [NSThread currentThread]);

        dispatch_semaphore_signal(sem);
    });
    dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
}

如果当创建信号量时传入值为1又会怎么样呢?
答:当i=0时,开启线程去执行任务后,wait信号量-1,但是此时信号量为0不会阻塞线程,所以进入i=1;
当i=1时,开启线程去执行任务后,wait信号量-1,此时信号量为-1阻塞线程,它将等待signal(即信号量+1)后再执行下去;
因此,若是并发队列时,当创建信号量为1时,任务1和任务2执行顺序不固定。

dispatch_source_t

dispatch_source_t主要用于计时操作,其原因是因为它创建的timer不依赖于RunLoop,且计时精准度比NSTimer高。

在iOS开发中一般使用NSTimer来处理定时逻辑,但NSTimer是依赖Runloop的,而Runloop可以运行在不同的模式下。如果NSTimer添加在一种模式下,当Runloop运行在其他模式下的时候,定时器就挂机了;假如当前Runloop处于阻塞状态,NSTimer触发时间就会推迟到下一个Runloop周期。因此NSTimer在计时上会有误差,并不是特别精确,而GCD定时器不依赖Runloop,计时精度要高很多。

dispatch_source是一种基本的数据类型,可以用来监听一些底层的系统事件,如:

Dispatch Source一共可以监听六类事件,分为11个类型:

主要使用的API:

/*
* 参数1(type):第一个参数用于标识Dispatch Source要监听的事件类型,共有11个类型
* 参数2(handle):取决于要监听事件的类型,比如监听的是Mach端口相关的事件,那该参数就是mach_port_t类型的Mach端口号;
* 参数3(mask):取决于要监听事件的类型,比如监听文件属性更改的事件,那么该参数就是标识文件的哪个属性,如:DISPATCH_VNODE_RENAME
* 参数4(queue):回调函数所在的队列
*/
dispatch_source_t dispatch_source_create(dispatch_source_type_t type, uintptr_t handle, uintptr_t mask, dispatch_queue_t _Nullable queue);
    
//示例
dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
/*
* 参数1(source):该参数为目标Dispatch Source
* 参数2(start):定时器的起始时间
* 参数3(interval):定时器的间隔时间,单位为纳秒
* 参数4(leeway):间隔时间的精度,单位为纳秒
*/
void dispatch_source_set_timer(dispatch_source_t source, dispatch_time_t start, uint64_t interval, uint64_t leeway);

//示例
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1.0*NSEC_PER_SEC, 0.1*NSEC_PER_SEC);
/*
* 参数1(source):目标Dispatch Source
* 参数2(handler):事件处理器
*/
void dispatch_source_set_event_handler(dispatch_source_t source, dispatch_block_t _Nullable handler);

//示例
dispatch_source_set_event_handler(self.timer, ^{
    NSLog(@"GCDTimer");
});
/*
* 参数1(source):目标Dispatch Source
* 参数2(value):需要合并的数据
*/
void
dispatch_source_merge_data(dispatch_source_t source, uintptr_t value);

//示例
dispatch_source_merge_data(source, 100)
/*
* 参数1(source):目标Dispatch Source
* 返回值:Dispatch Source待处理的数据
*/
uintptr_t dispatch_source_get_data(dispatch_source_t source);

//示例
dispatch_source_get_data(source)
/*
* 参数1(object):非活动的、未挂起的调度源对象
*/
void dispatch_resume(dispatch_object_t object);

//示例
dispatch_resume(timer)
/*
* 参数1(object):调度源对象
*/
void dispatch_suspend(dispatch_object_t object);

//示例
dispatch_suspend(timer)

NSOperation

NSOperation基于GCD的更高一层的封装,NSOperation需要配合NSOperationQueue来实现多线程。
NSOperation实现多线程步骤如下:

  1. 创建任务,将需要执行的任务封装到NSOperation对象中
  2. 创建队列,创建NSOperationQueue队列
  3. 将任务添加到队列中。将NSOperation对象添加到NSOperationQueue中。

基本使用:

NSBlockOperation* operation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"NSBlockOperation");
}];
NSOperationQueue* queue = [[NSOperationQueue alloc] init];
[queue addOperation:operation];

NSOperation类是一个抽象类,实际使用中使用的是NSOperation的子类。

NSOperationQueue* queue = [[NSOperationQueue alloc] init];
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doSomething:) object:@"NSInvocationOperation"];
[queue addOperation:invocationOperation];
NSOperationQueue* queue = [[NSOperationQueue alloc] init];
NSBlockOperation* operation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"NSBlockOperation1 - %@", [NSThread currentThread]);
}];
    
[operation addExecutionBlock:^{
    NSLog(@"NSBlockOperation2 - %@", [NSThread currentThread]);
}];
    
[operation addExecutionBlock:^{
    NSLog(@"NSBlockOperation3 - %@", [NSThread currentThread]);
}];
    
[queue addOperation:operation];
@interface HQOperation : NSOperation
@end

@implementation HQOperation
- (void)main{
    for(int i = 0; i < 3; i++){
        NSLog(@"[%d] %@", i, [NSThread currentThread]);
    }
}
@end

HQOperation* operation = [[HQOperation alloc] init];
[operation start];

NSOperation设置优先级

NSOperationQueue* queue = [[NSOperationQueue alloc] init];
NSBlockOperation* operation1 = [NSBlockOperation blockOperationWithBlock:^{
    [NSThread sleepForTimeInterval:3];
    NSLog(@"NSBlockOperation1 - %@", [NSThread currentThread]);
}];
    
NSBlockOperation* operation2 = [NSBlockOperation blockOperationWithBlock:^{
    [NSThread sleepForTimeInterval:3];
    NSLog(@"NSBlockOperation2 - %@", [NSThread currentThread]);
}];

//设置了最高优先级
operation2.qualityOfService = NSQualityOfServiceUserInteractive;
    
[queue addOperation:operation1];
[queue addOperation:operation2];

//执行结果
2021-04-20 15:21:42.698211+0800 Demo[24004:23167490] NSBlockOperation2 - <NSThread: 0x6000010df780>{number = 7, name = (null)}
2021-04-20 15:21:42.701567+0800 Demo[24004:23167487] NSBlockOperation1 - <NSThread: 0x600001088200>{number = 8, name = (null)}

设置并发数,在GCD的使用中,可以通过信号量的方式来设置并发数,同样,在NSOperation中,也可以轻松设置并发数。

NSOperationQueue* queue = [[NSOperationQueue alloc] init];
//设置最大并发数为3,即同时有3条线程执行任务
queue.maxConcurrentOperationCount = 3;
for(int i = 0; i<10; i++){
    [queue addOperationWithBlock:^{
        NSLog(@"[%d] -- %@", i, [NSThread currentThread]);
    }];
}

//执行结果
2021-04-20 15:29:07.062726+0800 Demo[24082:23173786] [0] -- <NSThread: 0x600002dc1900>{number = 3, name = (null)}
2021-04-20 15:29:07.062730+0800 Demo[24082:23173778] [1] -- <NSThread: 0x600002dcd280>{number = 7, name = (null)}
2021-04-20 15:29:07.062780+0800 Demo[24082:23173787] [2] -- <NSThread: 0x600002dcc1c0>{number = 6, name = (null)}
2021-04-20 15:29:07.062934+0800 Demo[24082:23173786] [3] -- <NSThread: 0x600002dc1900>{number = 3, name = (null)}
2021-04-20 15:29:07.062988+0800 Demo[24082:23173787] [5] -- <NSThread: 0x600002dcc1c0>{number = 6, name = (null)}
2021-04-20 15:29:07.062994+0800 Demo[24082:23173778] [4] -- <NSThread: 0x600002dcd280>{number = 7, name = (null)}
2021-04-20 15:29:07.063117+0800 Demo[24082:23173786] [6] -- <NSThread: 0x600002dc1900>{number = 3, name = (null)}
2021-04-20 15:29:07.063154+0800 Demo[24082:23173787] [7] -- <NSThread: 0x600002dcc1c0>{number = 6, name = (null)}
2021-04-20 15:29:07.063269+0800 Demo[24082:23173778] [8] -- <NSThread: 0x600002dcd280>{number = 7, name = (null)}
2021-04-20 15:29:07.064370+0800 Demo[24082:23173786] [9] -- <NSThread: 0x600002dc1900>{number = 3, name = (null)}

在NSOperation中,还可以添加依赖关系

NSOperationQueue* queue = [[NSOperationQueue alloc] init];
NSBlockOperation* operation1 = [NSBlockOperation blockOperationWithBlock:^{
    [NSThread sleepForTimeInterval:3];
    NSLog(@"请求数据1 - %@", [NSThread currentThread]);
}];
    
NSBlockOperation* operation2 = [NSBlockOperation blockOperationWithBlock:^{
    [NSThread sleepForTimeInterval:3];
    NSLog(@"根据数据1,请求数据2 - %@", [NSThread currentThread]);
}];
    
NSBlockOperation* operation3 = [NSBlockOperation blockOperationWithBlock:^{
    [NSThread sleepForTimeInterval:3];
    NSLog(@"根据数据2,请求数据3 - %@", [NSThread currentThread]);
}];
    
[operation2 addDependency:operation1];
[operation3 addDependency:operation2];
[queue addOperation:operation1];
[queue addOperation:operation2];
[queue addOperation:operation3];

//执行结果
2021-04-20 15:33:51.102025+0800 Demo[24165:23179016] 请求数据1 - <NSThread: 0x600002502380>{number = 7, name = (null)}
2021-04-20 15:33:54.104030+0800 Demo[24165:23179022] 根据数据1,请求数据2 - <NSThread: 0x600002572740>{number = 6, name = (null)}
2021-04-20 15:33:57.108527+0800 Demo[24165:23179016] 根据数据2,请求数据3 - <NSThread: 0x600002502380>{number = 7, name = (null)}
上一篇下一篇

猜你喜欢

热点阅读