iOS多线程实现

以下是对iOS实现多线程的介绍,阅读前需先对线程有一定的了解

线程生命周期


iOS多线程实现_第1张图片
线程生命周期

iOS实现多线程的方式

NSThread

GCD

NSOperation

NSThread实现多线程

NSThread是线程类,创建一个NSThread就是创建一个线程

NSThread创建线程的几种方式:

+ (void)detachNewThreadWithBlock:(void (^)(void))block;

- (instancetype)initWithBlock:(void (^)(void))block

+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;

- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument

detach开头的类方法,在线程创建 的同时,会运行线程。

实例初始化的线程,则需要主动调用start方法,启动线程。调用start方法之后,线程立即进入就绪状态,等待系统的调度,然后线程就在运行和就绪状态之间来回切换,直到事务完成。

这里的block和selector是线程需要执行的事务,当事务执行完成之后该线程就可以被释放了(死亡状态)。

除了事务执行,在线程出错,或者调用thread的exit方法后,线程也会dead。

线程状态

isExecuting:是否正在执行

isFinished:是否结束

isCanceled:是否取消

调用Thread的cancel方法,并不能真的取消线程事务,只能把isCanceled变成YES

如果需要取消线程,可以向线程发送一个信号(比如调用cancel方法,把isCanceled变成YES),在线程事务中判断这个信号,当线程收到终止信号,程序终止事务或者调用exit(例如事务是一个循环,在循环体中必要的地方判断isCanceled是否为YES,当为YES的时候跳出循环体,终止事务)。

尽量不要用exit结束线程(具体原因忘记了,等找到,再补回来)

线程睡眠

调用sleepXX 方法可以暂停线程一段时间,使线程进入阻塞状态。

+ (void)sleepUntilDate:(NSDate *)date;

+ (void)sleepForTimeInterval:(NSTimeInterval)ti;

线程优先级

threadPriority代表线程优先级,是一个double类型,区间为:0~1,1代表最高优先级,0代表最底优先级。

NSThread默认优先级是0.5。

优先级高的线程获取更多的执行机会。

线程同步与线程通信

使用@synchronized代码块实现同步

被@synchronized修饰的代码块可简称为同步代码块。

@synchronized(obj) {

...

}

同步代码块是为了防止多个线程同时访问同一个资源(obj),

obj就是同步监视器,只有获得了对obj的锁定(或者资源访问机会,类似对obj加了同步锁),才能执行下面的代码块。

要正确的选用监视器(obj)

代码块里的代码要尽可能的短,最好只放入访问和修改obj 的代码

同步锁(NSLock)

NSLock也是控制多线程对临界资源访问的工具,每次只有一个线程可以NSLock进行锁定。

[lock lock];

...

[lock unlock];

synchronized和NSLock锁的是什么?谁才是临界资源?

lock是锁,临界资源放在锁中间,大家都遵守协议(只有获取到锁,才可以访问临界资源)才能做到对临界资源的同步访问。

NSRecursiveLock(递归锁)

NSCondition线程通信

以上的同步方法都是被动同步,当线程访问的资源被锁定以后,之后被动排队,等待资源释放。NSCondition是线程的主动同步,例如:线程A的任务是基于线程B的任务的结果之上的,线程A利用NSCondition主动等待,当线程B的任务完成以后,利用NSCondition通知线程A,线程A收到通知后结束等待,完成自己的任务。

NSCondition也实现了NSLocking协议,所以NSCondition也可以当做锁来使用,并在锁的基础上加了wait、signal,broadcast等功能。普通的lock实现的被动同步是无序的,谁先获得资源谁先执行,加入了wait、signal,broadcast功能的Condition可以实现有序的同步。

例如,生产者和消费者同步。

使用NSLock,如果消费者先获取到产品库的使用权,会先消费,但是这时候还没有生产,产品库是空的,消费不了,然后释放使用权,接着生产者获取产品库使用权,生产产品。这显然不是一个正确的顺序。

使用NSCondition,如果消费者先获取到产品库的使用权,判断是否有产品,没有就wait等待,然后生产者获取使用权,开始生产,完成后signal通知消费者。消费者收到通知,开始消费。

- (void)wait;

让当前线程等待

- (void)signal;

通知某一个等待的线程,可以继续了

- (void)broadcast;

通知所有等待的线程,可以继续了

GCD实现多线程

GCD的优点

GCD可以管理线程。使用GCD实现多线程只需两步,创建队列,将任务提交给队列。队列负责管理开发者提交的任务,每一个队列都已一个线程池,用来管理线程。 控制线程的同步、并发、生命周期管理是一个非常复杂的过程,而对于开发者来说,更关心的是待处理的任务。GCD队列对多线程进行了封装,使得开发者从管理多线程的复杂工作中脱离出来,把每一个任务封装成一个block工作单元。开发者把这些工作单元放入GCD队列中,由队列来进行创建、管理线程的工作(这个过程对开发者不可见),这样开发者就可以专心处理自己的任务了。

队列的种类:

串行队列:线程池只有一个线程,任务以串行执行。

并发队列:线程池有多个线程,任务以FIFO的顺序并发启动执行。

创建队列

dispatch_queue_t dispatch_get_current_queue(void);

获取当然任务所在的队列

dispatch_queue_t dispatch_get_main_queue(void)

获取主队列:(一个串行队列,只有一个UI线程)

dispatch_queue_t dispatch_get_global_queue(long identifier, unsigned long flags);

获取系统全局并发队列。可以指定优先级。

dispatch_queue_t dispatch_queue_create(const char *_Nullable label, dispatch_queue_attr_t _Nullable attr);

创建一个串行或者并发队列

向队列提交任务

void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

异步向队列提交任务。

void dispatch_apply(size_t iterations, dispatch_queue_t queue,

DISPATCH_NOESCAPE void (^block)(size_t));

异步向队列提交多个相同的任务。串行的时候,可以理解为一个任务执行了多次,但是并发的时候,这几个任务是并发执行的。

void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block);

某个时刻,异步向队列提交任务。

NSOperation&NSOperationQueue实现多线程

优势

NSOperationQueue的实现原理和GCD类似,NSOperationQueue也有一个线程池,来管理多线程的操作,开发者只需要关注与任务的分发。与GCD不同的是,GCD的接口是C类型接口,NSOperationQueue的接口是面向对象类型的接口。GCD的每个任务是一段代码段,NSOperationQueue的每个任务是一个NSOperation对象。GCD的任务对比与NSOperation的优势是,简单方便,创建一个任务只需要一段断码段即可,而NSOperation需要创建一个对象,复杂的任务还需要实现一个NSOperation子类。GCD任务的劣势是,无法对任务进行控制,当一个任务提交到队列上以后,很难取消该任务,或者获取该任务的进度。而NSOperation可以有效控制任务的进度,甚至可以灵活地取消任务,甚至整个队列的任务都能取消。而且NSOperationQueue很容易控制最大并发数,GCD队列控制起来就比较复杂。NSOperationQueue还可以灵活地暂停任务的分发。

NSOperationQueue可以设置最大并发数,当最大并发数为1的时候,就是串行队列,>1的时候,就是并发队列。

队列

- (void)addOperation:(NSOperation *)op;

- (void)addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait NS_AVAILABLE(10_6, 4_0);

- (void)addOperationWithBlock:(void (^)(void))block NS_AVAILABLE(10_6, 4_0);

提交任务,任务也可以是一个简单的代码块。

@property NSInteger maxConcurrentOperationCount;

最大并发数

@property (getter=isSuspended) BOOL suspended;

暂停

@property (nullable, assign ) dispatch_queue_t underlyingQueue;

底层对应的GCD队列

- (void)cancelAllOperations;

取消队列中所有任务

- (void)waitUntilAllOperationsAreFinished;

阻塞当前线程,知道队列中的所有任务完成。

任务(操作)

NSOperation一般不会直接拿来用,而是实现它的子类,或者是用现有的几个简单的子类NSBlockOperation、NSInvocationOperation(以代码块、函数作为任务)。

- (void)start;

开始任务

- (void)main;

任务主体,子类要重写这个方法来实现自己的任务

@property (readonly, getter=isCancelled) BOOL cancelled;

是否被取消,由cancel方法来改变这个值

- (void)cancel;

取消任务,这个和NSThread里的cancel一样,只能改变cancelled的值,具体取消需要子类来完成。

@property (readonly, getter=isExecuting) BOOL executing;

是否正在执行

@property (readonly, getter=isFinished) BOOL finished;

是否执行完成

@property (readonly, getter=isConcurrent) BOOL concurrent;

是否并发

@property (readonly, getter=isAsynchronous) BOOL asynchronous

是否异步

@property (readonly, getter=isReady) BOOL ready;

是否就绪

- (void)addDependency:(NSOperation *)op;

- (void)removeDependency:(NSOperation *)op;

添加、移除,“基于”任务。(该任务是基于这些任务的,只有这些基础任务执行完,该任务才可以执行)

@property (nullable, copy) void (^completionBlock)(void)

任务结束回调,当任务结束时调用。

你可能感兴趣的:(iOS多线程实现)