本文的主要目的是介绍NSThread
、GCD
、NSOperation
常见的使用方式。
NSThread
NSthread
是苹果官方提供面向对象的线程操作技术,是对thread
的上层封装,比较偏向于底层。简单方便,可以直接操作线程对象,使用频率较少。它需要程序员自己管理线程的生命周期。
NSThread的使用如下
- 通过
init
初始化方式创建
//方式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];
- 通过
detachNewThread(xxx)
构造器方式创建
//方式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]);
}];
- 通过
performSelector(xxx)
方式创建
//在主线程执行
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray *)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 *)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;
- 线程使用栈区大小,默认是512K
@property NSUInteger stackSize;
- 线程调用栈信息
//该线程中函数调用的虚拟地址的数组
@property (class, readonly, copy) NSArray *callStackReturnAddresses;
//线程调用函数的名字数字
@property (class, readonly, copy) NSArray *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];
- 线程取消。调用-cancel方法并不会立刻取消线程,它仅仅是将cancelled属性设置为YES。cancelled也仅仅是一个用于记录状态的属性。线程取消的功能需要我们在main函数中自己实现
- (void)cancel;
[thread cancel];
- 如果创建NSThread时不指定target或者selector,那么main函数就会直接推出,如果都指定了,main函数会调用[target selector:argument],执行完成后退出。
当调用start时,start方法内部调用的就是main方法
。
- (void)main; // thread body method
[thread main];
类方法
- 线程阻塞(暂停)
+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
- 强制停止线程,线程进入死亡状态。该方法属于核弹级别终极API,调用之后会立即终止线程,即使任务还没有执行完成也会中断。这就非常有可能导致内存泄露等严重问题,所以
一般不推荐使用
。
+ (void)exit;
- 设置线程调度优先级 priority(优先级)。调度优先级的取值范围是
0.0 - 1.0
,默认0.5
,值越大,优先级越高。
+ (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] {number = 7, name = (null)}
2021-04-16 16:27:23.106189+0800 Demo[87930:20756012] [任务2] {number = 7, name = (null)}
2021-04-16 16:27:25.111495+0800 Demo[87930:20756012] [任务3] {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] {number = 7, name = (null)}
2021-04-16 16:27:39.119116+0800 Demo[87930:20756012] [任务5] {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] {number = 3, name = (null)}
2021-04-16 16:48:50.223659+0800 Demo[88208:20774080] [任务2] {number = 6, name = (null)}
2021-04-16 16:48:50.223659+0800 Demo[88208:20774083] [任务3] {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] {number = 9, name = (null)}
2021-04-16 16:48:52.222427+0800 Demo[88208:20774079] [任务4] {number = 4, name = (null)}
总结:
-
dispatch_group_async
函数是自动将任务添加到任务组中。 -
dispatch_group_enter
函数是手动将任务添加到任务组中。 - 当通过
dispatch_group_enter
函数被调用后,dispatch_group_leave
函数调用前,其间的所有任务都被添加到任务组中。 - 当任务所在的队列为
串行队列
时,会等待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] {number = 6, name = (null)}
2021-04-19 09:46:20.458536+0800 Demo[13733:22451112] [任务2] {number = 6, name = (null)}
2021-04-19 09:46:22.463882+0800 Demo[13733:22451112] [任务3] {number = 6, name = (null)}
2021-04-19 09:46:22.464409+0800 Demo[13733:22451112] ------------栅栏任务------------{number = 6, name = (null)}
2021-04-19 09:46:22.464688+0800 Demo[13733:22451112] [任务4] {number = 6, name = (null)}
2021-04-19 09:46:22.464940+0800 Demo[13733:22451112] [任务5] {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] {number = 5, name = (null)}
2021-04-19 09:47:09.266121+0800 Demo[13753:22452477] [任务1] {number = 8, name = (null)}
2021-04-19 09:47:09.266191+0800 Demo[13753:22452478] [任务3] {number = 3, name = (null)}
2021-04-19 09:47:09.266493+0800 Demo[13753:22452478] ------------栅栏任务------------{number = 3, name = (null)}
2021-04-19 09:47:09.266699+0800 Demo[13753:22452478] [任务4] {number = 3, name = (null)}
2021-04-19 09:47:09.266762+0800 Demo[13753:22452474] [任务5] {number = 5, name = (null)}
从上面代码和结果分析可知,dispatch_barrier_async
函数的主要作用是等待队列中,位于dispatch_barrier_async
函数前的任务完成之后,再执行dispatch_barrier_async
函数后的任务。
在串行队列中,任务本身就是按顺序执行,因此,栅栏函数的作用不明显。而在并发队列中,使用栅栏函数可以很好的控制队列内任务执行的顺序。
重点:dispatch_barrier_async
和dispatch_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是一种基本的数据类型,可以用来监听一些底层的系统事件,如:
Timer Dispatch Source:定时器事件源,用来生成周期性的通知或回调
Signal Dispatch Source:监听信号事件源,当有UNIX信号发生时会通知
Descriptor Dispatch Source:监听文件或socket事件源,当文件或socket数据发生变化时会通知
Process Dispatch Source:监听进程事件源,与进程相关的事件通知
Mach port Dispatch Source:监听Mach端口事件源
Custom Dispatch Source:监听自定义事件源
Dispatch Source一共可以监听六类事件,分为11个类型:
DISPATCH_SOURCE_TYPE_DATA_ADD:属于自定义事件,可以通过dispatch_source_get_data函数获取事件变量数据,在我们自定义的方法中可以调用dispatch_source_merge_data函数向Dispatch Source设置数据,下文中会有详细的演示。
DISPATCH_SOURCE_TYPE_DATA_OR:属于自定义事件,用法同上面的类型一样。
DISPATCH_SOURCE_TYPE_MACH_SEND:Mach端口发送事件。
DISPATCH_SOURCE_TYPE_MACH_RECV:Mach端口接收事件。
DISPATCH_SOURCE_TYPE_PROC:与进程相关的事件。
DISPATCH_SOURCE_TYPE_READ:读文件事件。
DISPATCH_SOURCE_TYPE_WRITE:写文件事件。
DISPATCH_SOURCE_TYPE_VNODE:文件属性更改事件。
DISPATCH_SOURCE_TYPE_SIGNAL:接收信号事件。
DISPATCH_SOURCE_TYPE_TIMER:定时器事件。
DISPATCH_SOURCE_TYPE_MEMORYPRESSURE:内存压力事件
主要使用的API:
- dispatch_source_create: 创建事件源
/*
* 参数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);
- dispatch_source_set_timer:给监听事件类型为DISPATCH_SOURCE_TYPE_TIMER的Dispatch Source设置相关属性
/*
* 参数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);
- dispatch_source_set_event_handler: 设置数据源回调。
当Dispatch Source监听到事件时,会调用指定的回调函数或者闭包,该回调函数和闭包就是Dispatch Source的事件处理器,我们可以通过dispatch_source_set_event_handler或者dispatch_source_set_event_handler_f给创建好的Dispatch Source设置处理器,其中前者是设置闭包形式的处理器,后者是设置函数形式的处理器。
/*
* 参数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");
});
- dispatch_source_merge_data: 设置事件源数据。
将数据合并到Dispatch Source中,并将handler提交到目标队列queue再执行。
/*
* 参数1(source):目标Dispatch Source
* 参数2(value):需要合并的数据
*/
void
dispatch_source_merge_data(dispatch_source_t source, uintptr_t value);
//示例
dispatch_source_merge_data(source, 100)
- dispatch_source_get_data: 获取事件源数据
/*
* 参数1(source):目标Dispatch Source
* 返回值:Dispatch Source待处理的数据
*/
uintptr_t dispatch_source_get_data(dispatch_source_t source);
//示例
dispatch_source_get_data(source)
- dispatch_resume: 继续
刚创建好的Dispatch Source是处于暂停状态的,需要手动调用dispatch_resume函数将其启动。
/*
* 参数1(object):非活动的、未挂起的调度源对象
*/
void dispatch_resume(dispatch_object_t object);
//示例
dispatch_resume(timer)
- dispatch_suspend: 挂起,将Dispatch Source暂时挂起,挂起后可通过dispatch_resume重新激活。
/*
* 参数1(object):调度源对象
*/
void dispatch_suspend(dispatch_object_t object);
//示例
dispatch_suspend(timer)
NSOperation
NSOperation
是基于GCD
的更高一层的封装,NSOperation需要配合NSOperationQueue
来实现多线程。
NSOperation实现多线程步骤如下:
- 创建任务,将需要执行的任务封装到NSOperation对象中
- 创建队列,创建NSOperationQueue队列
- 将任务添加到队列中。将NSOperation对象添加到NSOperationQueue中。
基本使用:
NSBlockOperation* operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"NSBlockOperation");
}];
NSOperationQueue* queue = [[NSOperationQueue alloc] init];
[queue addOperation:operation];
NSOperation
类是一个抽象类,实际使用中使用的是NSOperation
的子类。
- NSInvocationOperation类
NSOperationQueue* queue = [[NSOperationQueue alloc] init];
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doSomething:) object:@"NSInvocationOperation"];
[queue addOperation:invocationOperation];
- NSBlockOperation类
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];
- 自定义NSOperation类
自定义NSOperation类可以通过实现内部相应的方法来封装任务。
@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 - {number = 7, name = (null)}
2021-04-20 15:21:42.701567+0800 Demo[24004:23167487] NSBlockOperation1 - {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] -- {number = 3, name = (null)}
2021-04-20 15:29:07.062730+0800 Demo[24082:23173778] [1] -- {number = 7, name = (null)}
2021-04-20 15:29:07.062780+0800 Demo[24082:23173787] [2] -- {number = 6, name = (null)}
2021-04-20 15:29:07.062934+0800 Demo[24082:23173786] [3] -- {number = 3, name = (null)}
2021-04-20 15:29:07.062988+0800 Demo[24082:23173787] [5] -- {number = 6, name = (null)}
2021-04-20 15:29:07.062994+0800 Demo[24082:23173778] [4] -- {number = 7, name = (null)}
2021-04-20 15:29:07.063117+0800 Demo[24082:23173786] [6] -- {number = 3, name = (null)}
2021-04-20 15:29:07.063154+0800 Demo[24082:23173787] [7] -- {number = 6, name = (null)}
2021-04-20 15:29:07.063269+0800 Demo[24082:23173778] [8] -- {number = 7, name = (null)}
2021-04-20 15:29:07.064370+0800 Demo[24082:23173786] [9] -- {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 - {number = 7, name = (null)}
2021-04-20 15:33:54.104030+0800 Demo[24165:23179022] 根据数据1,请求数据2 - {number = 6, name = (null)}
2021-04-20 15:33:57.108527+0800 Demo[24165:23179016] 根据数据2,请求数据3 - {number = 7, name = (null)}