关闭

ios开发系列之多线程的三种实现方法

标签: ios开发多线程
170人阅读 评论(0) 收藏 举报
分类:

这篇文章中,我不会说多线程是什么、线程和进程的区别、多线程有什么用,当然我也不会说什么是同步、什么是异步等问题,以及UI为什么要在主线程中刷新。仅仅谈一下iOS 中多线程有三种实现方法:NSThread ,NSOperation ,GCD

一. NSThread 继承于NSObject

1.创建分线程

1.1)使用实例方法创建

//在分线程执行self的run:方法,传入一个参数
NSThread * thread = [[NSThread alloc]initWithTarget:self selector:@selector(run:) object:@"传的参数"];
//这种方法需要手动开启分线程
[thread start]; //开启分线程

1.2)使用静态方法创建

[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"123456"];//这种方法自动开启分线程

1.3).使用performSelectorInBackground: 创建一个分线程

[self performSelectorInBackground:@selector(run:) withObject:@"0000"];

之后我们就可以在- (void)run:(id)sender;方法中执行分线程中的内容.该方法的参数为创建时传的参数

2.回到主线程

[self performSelectorOnMainThread:@selector(backToMainThread:) withObject:sender waitUntilDone:NO];
这里`waitUntilDone:`参数表示是否等待@selector方法中的内容执行完毕再执行分线程中的内容.YES 等待 ,NO 不等待,两者并发执行

另外NSThread 常见的属性与方法:

//判断是否为主线程,返回YES则为主线程
@property (readonly) BOOL isMainThread;
//线程的名字
@property (copy) NSString *name;
//判断某个线程的三种状态属性
@property (readonly, getter=isExecuting) BOOL executing;
@property (readonly, getter=isFinished) BOOL finished;
@property (readonly, getter=isCancelled) BOOL cancelle;
//取消线程
- (void)cancel;
//启动线程
- (void)start;
//获取当前线程的信息
+ (NSThread *)currentThread;
//获取主线程的信息
+ (NSThread *)mainThread;
//使当前线程睡眠(暂停)一段时间
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
//使当前线程睡眠到某一时刻
+ (void)sleepUntilDate:(NSDate *)date;
//终结当前线程
+ (void)exit;

二. GCD (Grand Central Dispatch)

GCD使用队列(queue)来管理线程.

队列分为两种:
1.运行在主线程的队列,一次只能执行一个任务,遵循先进先出(FIFO)原则,称之为串行队列

dispatch_queue_t mainQueue = dispatch_get_main_queue();//获得 mainQueue 队列。

2.运行在分线程的队列,同时可以执行多个任务,实际运行的时候使用哪一个分线程,我们是不能控制的. 称之为(全局)并行队列

dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //第二个参数为预留参数,填0
globalQueue的四个优先级
//#define DISPATCH_QUEUE_PRIORITY_HIGH 2
//#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
//#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
//#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN

我们只需要把任务提交到队列中,任务就会在对应的线程中执行了.

提交方法分为两种:
1.同步提交 (sync): 该操作会阻塞当前线程,并等待Block中的任务执行完毕后才会继续执行后续代码,如果是在主线程中使用,会导致线程死锁,有关死锁现象在本节最后会有解释

dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);//第一个参数是我们要提交的队列

2.异步提交 (async):该操作不会阻塞当前线程,线程之间同时执行,一般只使用这种方法

dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

通常这样使用 GCD

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //去分线程执行代码
        dispatch_async(dispatch_get_main_queue(), ^{
           //切回主线程
        });
    });

另外GCD中常用的方法
1.dispatch_group_async(dispatch_group_t group,dispatch_queue_t queue,dispatch_block_t block);
队列组可以实现监听一组任务是否完成,完成后得到通知执行其他的操作.我们可以在所有任务都下载完成时通知我们下载完成,这是一个很实用的功能

 //等待多个任务执行完毕后,再去执行另外一个任务。
 dispatch_group_t group = dispatch_group_create();
 dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    //执行任务1
 });
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    //执行任务2
 });
 dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    //等待 group 中的全部任务执行完毕,才会执行这里的任务。
 });

2.dispatch_apply(size_t iterations, dispatch_queue_t queue,void (^block)(size_t));//第一个参数表示执行次数

执行某个代码片段N次
dispatch_apply(5, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t index) {
        NSLog(@"执行多次");
    });

3.dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);
使用该方法可以自定义队列,第一个参数是一个C语言的字符串,表示给queue一个标签(名字,标识符),用于Debug时标识唯一的队列,可以为空
第二个参数有两种选择:

DISPATCH_QUEUE_SERIAL //串行  或者填NULL
DISPATCH_QUEUE_CONCURRENT //并行
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);

4.dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);

如果传入的queue是通过DISPATCH_QUEUE_CONCURRENT参数 自己创建的queue时,这个方法会阻塞这个queue(注意不是当前线程)
只有等到前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行,也就是说它执行完毕后会取消这个阻塞
如果是其他的queue,那么它就和dispatch_async一样
barrier称为只写锁,其他同步或者异步都是并发写操作的,如果是只读的用sync 和async就行,如果需要并发写操作,就要注意barrier了

死锁现象

//以下代码在主线程调用
dispatch_queue_t mainQueue = dispatch_get_main_queue();
NSLog(@"之前============");
dispatch_sync(mainQueue, ^{
  NSLog(@"之间----------");
});
  NSLog(@"之后++++++++");

我们会看到输出结果只会输出第一句,而且主线程已经被卡死.如果你在界面上放一个按钮,你就会发现点不了了
解释:因为这里使用的是同步提交dispatch_sync,然后把Block 中的任务放到指定的队列中执行.只有等到Block中的任务完成后才会让当前线程继续往下执行
而这里指定的队列是main_queue,可是main_queue中的任务会被取出来放到主线程中执行,但是主线程这个时候已经被阻塞了,所以Block中的任务就不能完成
Block中的任务没法完成, dispatch_sync就会一直阻塞主线程,形成死锁现象.导致主线程一直卡死

三. NSOperation 的子类 NSInvocationOperation 与NSBlockOperation

NSOperation 是一个抽象类,我们不能直接使用它的对象,必须使用它的子类的对象,该类是苹果对GCD的封装,完全面向对象.
这里 NSOperation的子类的对象 对应 GCD中的任务 ,而NSOperationQueue对应GCD中的队列
还是和GCD一样,我们先介绍

队列NSOperationQueue

1.主队列,添加到主队列的任务会一个接一个的排着队在主线程中处理

NSOperationQueue * queue = [NSOperationQueue mainQueue];

2.其他队列,该队列中的任务会在其他线程中并发执行

NSOperationQueue * queue = [[NSOperationQueue alloc]init];

重要属性介绍

@property (readonly) NSUInteger operationCount//获取队列的任务数

@property (getter=isSuspended) BOOL suspended //是否挂起队列 YES 表示暂停 NO表示继续

@property NSInteger maxConcurrentOperationCount;
该属性表示可以同时执行的任务(或称为操作)的最大数目。
此属性的值只影响当前队列在同一时间执行的操作。其他操作队列也可以并行执行其最大数量的操作。
设置该属性值为1就是相当于GCD中的串行, 大于1就相当于并行

重要方法介绍

//添加依赖
- (void)addDependency:(NSOperation *)op;
该方法表示添加两个任务之间的依赖.让一个任务(该方法调用对象)等待另一个任务(该方法传入参数对象)操作完成后再进行操作
不能相互添加依赖,会出现死锁现象,
可以用removeDependency来解除依赖关系,
可以在不同队列之间依赖,因为依赖的对象是任务,和队列没关系

//添加一个任务
- (void)addOperation:(NSOperation *)op;

//添加多个任务  wait参数如果是YES的话,当前线程会被阻塞,直到所有指定的任务完成执行
- (void)addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait;

- (void)addOperationWithBlock:(void (^)(void))block;

NSOperation的主要属性与方法

@property (copy) void (^completionBlock)(void);//用来设置完成后需要执行的操作

1.NSInvocationOperation

NSInvocationOperation * invocationOperation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run:) object:@"123"];

然后将invocationOperation添加到队列中就会自动执行

2.NSBlockOperation

NSBlockOperation* blockOperation = [NSBlockOperation blockOperationWithBlock:^{
  }];

addExecutionBlock,并发的执行多个 block 任务,系统自动分配到当前空闲的线程,可能是主线程也可能是分线程。

[blockOperation addExecutionBlock:^{
  }];

然后将该对象添加到队列中就会自动执行

我们也可以自定义 NSOperation 子类,重写 main 方法,然后把 operation 对象添加到 NSOperationQueue 中,就会自动在分线程执行 main 方法。

总结三种实现方法:NSThread ,NSOperation ,GCD

NSThread,API 简单方便,功能也简单,需要我们自己去管理线程的生命周期和资源加锁。不易管理多个线程间的关系。

GCD,能够充分利用多核处理器的性能,使用起来简单方便,让我们专注于多线程任务的开发。使用队列来管理线程,队列分两种,主线程 mainQueue 和 四个优先级不同的分线程队列 globalQueue。我们使用 dispatch_async 把 block 任务提交到队列中,block 任务就会在对应的线程中执行了。

NSOperation,是一个抽象类,我们不能直接使用,需要使用它的子类。系统提供了两个子类,NSInvocationOperation,NSBlockOperation。但是不常用。我们通常自定义它的子类,自定义的时候重写 main 方法。只要我们把 operation 对象添加到 NSOperationQueue 就会自动的在分线程中执行 main 方法了。

0
0

猜你在找
【直播】机器学习&深度学习系统实战(唐宇迪)
【直播】Kaggle 神器:XGBoost 从基础到实战(冒教授)
【直播回放】深度学习基础与TensorFlow实践(王琛)
【直播】计算机视觉原理及实战(屈教授)
【直播】机器学习之凸优化(马博士)
【直播】机器学习之矩阵(黄博士)
【直播】机器学习之概率与统计推断(冒教授)
【直播】机器学习之数学基础
【直播】TensorFlow实战进阶(智亮)
【直播】深度学习30天系统实训(唐宇迪)
查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:54841次
    • 积分:1206
    • 等级:
    • 排名:千里之外
    • 原创:67篇
    • 转载:4篇
    • 译文:0篇
    • 评论:1条
    文章分类