iOS - 知识梳理(多线程)

多线程:一个进程里面开启多条线程,每条线程可以单独的执行不同的任务。

iOS实现多线程的方式:

1、pthread(C写的、基本不用) 2、NSThread 3、gcd 4、NSOperation

下面分别介绍下后三个常用的多线程方式

NSThread:

使用方式

// 方式1
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil];
[thread start];// 开启

// 方式2
[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];

// 方式3
[self performSelectorInBackground:@selector(run) withObject:nil];

介绍NSThread,要首先介绍一下线程的生命周期:新建-就绪-运行-阻塞-死亡

优点:比较轻量,使用方式更加灵活,可以很直观的控制线程对象。例如直接取消线程,也可以自定线程。

缺点:需要自己管理线程的生命周期、线程同步。

解释一下线程同步:多条线程按顺序执行任务。NSThread通过加锁实现,加锁对系统资源有一定的消耗。

下面的两种方式不用关心线程管理,数据同步的问题。

 

GCD:

使用方式

dispatch_queue_t queue = dispatch_queue_create("queue.concurrent", DISPATCH_QUEUE_CONCURRENT); // 串行队列

dispatch_queue_t queue = dispatch_queue_create("queue.serial", DISPATCH_QUEUE_SERIAL); //并行队列

dispatch_queue_t queue =  dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);//获取全局的并发队列

dispatch_queue_t  queue = dispatch_get_main_queue();//获取主队列 

dispatch_sync(queue, ^{ // 同步函数 要求马上执行
    //do something        
});

dispatch_async(queue, ^{ // 异步函数 等主线程执行完,在开线程执行任务
    // do something
});

GCD是苹果为多核并行运算提出的解决方案,会自动利用多核,自动管理线程的生命周期(创建,调度,销毁)。

那么GCD是如何自己管理生命周期和线程同步的问题呢,有两个概念 队列(queue) 和 任务 (task,上面的block),

使用方式已经在上面列出,下面总结一下两个函数和各种队列的使用效果:

首先是同步函数dispatch_sync,无论是并行队列还是串行队列,都不会开启新线程,并同步执行。

异步函数dispatch_async在串行(非主队列)或并行队列中都会开启新线程,不同的是串行队列里是串行执行任务,异步队列是并发执行任务。如果是在主队列,不开启新线程,串行执行任务。

 下面说一下死锁的问题:

dispatch_sync有个特性是不等当前任务执行完成立即开启下个任务,如果下个任务还是在当前队列执行任务,就会造成相互等待(死锁)。

举个例子

// 当前在主队列里

dispatch_queue_t queue =  dispatch_get_main_queue(); // 获取主队列

dispatch_sync(queue, ^{ 
       NSLog(@"---download1---%@",[NSThread currentThread]);
});
//同步执行任务,这时候主队列停止,等待sync添加的任务,
而sync添加的任务是在当前队列里执行NSLog,
NSLog又要等当前的队列执行完上个任务才能执行,就陷入了相互等待。。。

线程之间通信:

dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
        // 异步执行任务
      dispatch_async(dispatch_get_main_queue(), ^{ // 回到主队列
            NSLog(@"%@",[NSThread currentThread]);
      });
}); 

取消任务

iOS8之后可以调用dispatch_block_cancel来取消(需要注意必须用dispatch_block_create创建dispatch_block_t) 

dispatch_queue_t queue = dispatch_queue_create("com.test", DISPATCH_QUEUE_CONCURRENT);

dispatch_block_t block = dispatch_block_create(0, ^{
        NSLog(@"block1 %@",[NSThread currentThread]);
});
dispatch_async(queue, block); 
dispatch_block_cancel(block);

需要注意的是这种方式只能取消还没开始的任务

第二种取消方式就是模仿NSOperation里面的isCanceled。就是执行任务的时候加判断

dispatch_queue_t queue = dispatch_queue_create("com.test", DISPATCH_QUEUE_CONCURRENT);

__block BOOL isCancel = NO;

dispatch_async(queue, ^{
    sleep(3);
    if(isCancel){
        // 任务取消了
    }else{
        // 没有取消,继续执行
    }
});

 NSOperation

NSOperation是对gcd的封装,面向对象,并多了一些简单的功能。

NSOperation和NSOperationQueue实现多线程的具体步骤
1.将需要执行的操作封装到一个NSOperation对象中
2.将NSOperation对象添加到NSOperationQueue中
系统会自动将NSOperationQueue中的NSOperation取出来,并将取出的NSOperation封装的操作放到一条新线程中执行

NSOperation是个抽象类,要想使用必须继承该类,系统提供了两个直接能用的类

NSInvocationOperation

NSInvocationOperation *op  = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download) object:nil];
[op start];

NSBlockOperation

NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
       NSLog(@"@",[NSThread currentThread]); 
}];
[op addExecutionBlock:^{
        NSLog(@"%@",[NSThread currentThread]);
    }];
[op start];

 自定义类例如 MyOperation 继承NSOperation 

实现main方法

NSOperationQueue:

NSOperation可以调用start方法来执行任务,但默认是同步执行的
如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作

下面是添加到queue的方法

- (void)addOperation:(NSOperation *)op;
- (void)addOperationWithBlock:(void (^)(void))block;

还有三个方法重点说一下

  1. maxConcurrentOperationCount 设置大的并发数
  2. suspended 暂停任务
  3. cancelAllOperations 取消所有任务,这里也是只能取消当前没有开始的任务。(要想取消当前的任务,需要在任务里随时判断isCanceled变量)

通过上面的总结,希望能回答两个问题(1、iOS实现多线程的方式,各自的特点,优缺点 2,多线程使用要注意什么,gcd为什么会造成死锁?)。

多线程应用的好,可以提升app的运行效率,流畅度,运用不好的话也会有许多的负面效果,比如开启线程是很消耗系统资源的,不能无限制开启,再比如死锁等问题。所以以后在项目里要灵活运用多线程。

(最近就遇到一个问题,搜索列表问题,随着用户的输入,实时显示搜索到的列表(本地数据,实时过滤,当然量比较大),如果只是运用gcd的get_global_queue实现多线程的话会有问题,因为任务回调回来的时机不同,会造成显示的列表不对。然后我就用了Operation来解决的这个问题,当来新的搜索任务也就是用户输入改变的时候,如果上个任务还没有完成,则取消之前的任务。)

猜你喜欢

转载自blog.csdn.net/evol_f/article/details/82856315