多线程编程
一、多线程的基本概念:
- 进程:可以理解成一个运行中的应用程序,是系统进行资源分配和调度的基本单位,是操作系统结构的基础,主要管理资源。
线程:是进程的基本执行单元,一个进程对应多个线程。 - 主线程:处理UI,所有更新UI的操作都必须在主线程上执行。不要把耗时操作放在主线程,会卡界面。
- 多线程:在同一时刻,一个CPU只能处理1条线程,但CPU可以在多条线程之间快速的切换,只要切换的足够快,就造成了多线程一同执行的假象。
二、多线程的解决方案
在iOS中,目前有4套解决方案:pthreads,NSThread,GCD,NSOperation;
Pthreads
这是一套基于C语言的多线程API,适用于Unix/Liunx/Windows等系统,可移植性强,但使用难度大。
NSThread
这套方案是经过苹果封装后,完全面向对象的。可以直接操控线程对象,但是生命周期需要手动管理。
NSThread有三种创建方式:
init方式,需要手动启动
- (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));
- (instancetype)initWithBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
detachNewThreadSelector创建好之后自动启动
+ (void)detachNewThreadWithBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;
performSelectorInBackground创建好之后也是直接启动
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
NSOperation
1.NSOperation和NSOperationQueue
NSOperation是苹果对GCD的封装,完全面向对象,NSOperation和NSOperationQueue分别对应GCD中的任务和队列。
将要执行的任务封装到一个NSOperation对象中,将此任务添加到一个NSOperationQueue对象中,然后系统就会自动执行。
2.添加任务
NSOperation
NSOperation只是一个抽象类,不能直接使用。它提供了2个子类用于封装任务,分别是:NSInvocationOperation和NSBlockOperation。创建一个Operation后,需要调用start
方法启动任务,默认在当前队列同步执行,取消任务时调用cancel
方法。
- NSInvocationOperation需要传入一个方法名。
- (nullable instancetype)initWithTarget:(id)target selector:(SEL)sel object:(nullable id)arg;
- NSBlockOperation通过类方法创建
NSBlockOperation有一个方法addExecutionBlock:
,通过这个方法可以给Operation添加多个执行Block,这样程序会并发执行,会在主线程和其他多个线程执行,该方法必须在start
方法之前执行。
+ (instancetype)blockOperationWithBlock:(void (^)(void))block;
- (void)addExecutionBlock:(void (^)(void))block;
- 自定义Operation:通过继续NSOperation类,并实现起
main
方法,除此之外还要实现start
和cancel
在内的各种方法。
NSOperationQueue
NSOperationQueue只有两种队列:主队列、其他队列。其他队列包含了串行和并发。
NSOperationQueue中有个参数叫最大并发数:maxConcurrentOperationCount
,默认为-1,直接并发执行,当为1时表示不开启新线程,串行执行。
GCD
1.GCD特点
GCD会自动利用更多的CPU内核
GCD自动管理线程的生命周期(创建线程,调度任务,销毁线程等)
程序员只需要告诉 GCD 想要如何执行什么任务,不需要编写任何线程管理代码
2.GCD的基本概念
- 任务(block):任务就是将要在线程中执行的代码,将这段代码用block封装好,然后将这个任务添加到指定的执行方式(同步执行和异步执行),等待CPU从队列中取出任务放到对应的线程中执行。
- 同步(sync):一个接着一个,前一个没有执行完,后面不能执行,不开线程。
- 异步(async):开启多个新线程,任务同一时间可以一起执行。异步是多线程的代名词
- 队列:装载线程任务的队形结构。(系统以先进先出的方式调度队列中的任务执行)。在GCD中有两种队列:串行队列和并发队列。
- 并发队列:任务会按照FIFO的顺序取出来,取出来后加入到一个线程,然后取另一个任务放到另一个线程,由于速度很快,看起来所有任务一起执行,实际是CPU在多条线程之间快速切换
- 串行队列:串行队列的任务会按照FIFO的顺序一个个取出来,执行完一个继续执行另一个。
3.创建队列
使用dispatch_queue_create
来创建队列对象,传入两个参数,第一个参数表示队列的唯一标识,可为空。第二个参数用来表示串行队列(DISPATCH_QUEUE_SERIAL)或并发队列(DISPATCH_QUEUE_CONCURRENT)。
dispatch_queue_create(const char *_Nullable label,
dispatch_queue_attr_t _Nullable attr);
GCD还有另外两种队列:
主队列:用于刷新UI。
dispatch_get_main_queue()
全局并发队列:只要是并行任务一般都会加入到这个队列,这是系统提供的一个并发队列。
dispatch_get_global_queue(long identifier, unsigned long flags);
4.创建任务
同步任务:会阻塞当前线程,使用dispatch_sync
创建
dispatch_sync(dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block);
异步任务:不会阻塞当前线程,使用dispatch_async
创建
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
5.GCD其他方法
当任务需要异步进行,但是这些任务需要分成两组来执行时,可以使用dispatch_barrier_async
。
dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
当任务需要等待一段时间后执行时,可以使用dispatch_after
dispatch_after(dispatch_time_t when,
dispatch_queue_t queue,
dispatch_block_t block);
GCD中有个快速迭代的方法dispatch_apply
,可以同时遍历多个数字。
dispatch_apply(size_t iterations, dispatch_queue_t queue,
DISPATCH_NOESCAPE void (^block)(size_t));
异步执行几个耗时操作,当几个操作完成时回到主线程操作可以使用dispatch_group
。所有队列组中的并发任务并不按照顺序执行,当操作都完成时会调用dispatch_group_notify
函数。
dispatch_group_async(dispatch_group_t group,
dispatch_queue_t queue,
dispatch_block_t block);
三、多线程的其他用法
- 线程同步
所谓线程同步就是为了防止多个线程抢夺同一个资源造成的数据安全问题所采取的一种措施。有以下几种实现方法
互斥锁:给需要同步的代码块加一个互斥锁,@synchronized(锁对象){//需要锁定的代码}
同步执行:将多个线程需要执行代码加到同一个串行队列。 - 计时器
通过dispatch_source_t创建一个timer放到队列里面,通过dispatch_source_set_timer设置timer的首次执行时间,执行时间间隔,精确度,通过dispatch_source_set_event_handler设置timer执行的事件。
代码如下:
__block int count = 0;
//创建一个定时器
__block dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
//设置启动时间
dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, 0);
//设置定时器各项参数
dispatch_source_set_timer(timer, start, (int64_t)(1.0 * NSEC_PER_SEC), 0);
// 设置回调
dispatch_source_set_event_handler(timer, ^{
NSLog(@"%d----%@", count,[NSThread currentThread]);
count++;
if (count == 4) {
// 取消定时器
dispatch_cancel(timer);
timer = nil;
NSLog(@"定时器销毁");
}
});
//启动定时器
dispatch_resume(timer);
- 单例模式
{
static Tool *_instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken,^{
_instance = [[Tool alloc] init];
});
return _instance;
}
参考:完整项目资料下载