IOS——多线程

##一、基本概念ios

  • 什么是进程: 进程是在系统运行的一个程序,每一个进程之间是独立的,每一个进程均运行在其专有且受保护的内存空间内。git

  • 什么是线程: 一个进程想要执行任务,必须得有线程(至少一个线程),线程是进程的基本执行单位,一个进程的全部任务都必须在线程中执行。github

  • 线程的串行: 一个线程中任务的执行是串行的,若是要在一个线程中执行多个任务,只能一个一个的按顺序执行编程

##二、多线程安全

  • 什么是多线程:服务器

    一个进程中能够开启多个线程,每一个线程能够并发/并行执行不一样的任务,多线程能够提交程序的执行效率,好比同时执行任务ABC。多线程

  • 多线程原理:并发

    同一时间,CPU只能执行一个线程,只有一个线程正在执行,多线程并发执行,实际上是CPU快速的在多个线程之间切换,若是CPU的切换线程的时间足够快,就会形成多线程并发执行的假象。框架

  • 多线程的优缺点:异步

优势: 1.能适当的提升程序的执行效率 2.能适当的提升资源的利用率 缺点: 1.开启线程会占用必定的内存空间(主线程1M,子线程0.5M),若是开启过多的线程就会占用大量的内存空间,下降程序的性能。 2.线程越多,CPU在调度线程上的开销就越大。

##三、主线程

一个IOS程序运行之后,默认会开启一个线程,这个线程就被称为主线程或(UI线程)。主线程的主要做用是显示/刷新UI界面,处理UI事件(点击,滚动,拖拽等)。

IOS中的多线程:

iOS中有四种多线程编程的技术:

1.Pthread (基本不会使用,线程的生命周期由咱们⾃己管理)

2.NSThread(每一个Thread对象对应⼀一个线程)(使⽤用得⽐比较少,线程的⽣生命周期由咱们⾃己管理)

3.NSOperation(面向对象的线程技术)(基于gcd来实现,常常使⽤,⽣命周期由系统管理)

4.GCD(是基于C语⾔言的框架,能够充分利用多核,是苹果推荐使⽤的多线程技术)(常用,生命周期由系统管理))

以上这四种编程⽅方式从上到下,抽象度层次是从低到高的,抽象度越高的使用越简单,也是Apple最推荐使用的。可是就目前而言,iOS的开发者,须要了解三种多线程技术的基本使⽤用过程。由于不少 框架技术分别使用了不一样多线程技术。

##四、NSThread

建立线程、执行下载任务,须要start手动开启执行

NSThread *thread_A = [[NSThread alloc] initWithTarget:self selector:@selector(run_A) object:nil];
    thread_A.name = @"线程A";

启动任务

[thread_A start];

阻塞线程,等待几秒,是这个线程里面的全部任务一块儿阻塞

[NSThread sleepForTimeInterval:5];

阻塞到某个时间

[NSThread sleepUntilDate:[[NSDate date] dateByAddingTimeInterval:1000]];

方式二:指定任务直接执行不须要手动开启 start

[NSThread detachNewThreadSelector:@selector(run_A) toTarget:self withObject:nil];

取消进程

[thread_A cancel];

获取当前线程信息
+ (NSThread *)currentThread;
获取主线程信息
+ (NSThread *)mainThread;

ps:若是是有关UI更新的操做,在其它线程中处理完以后要回到主线程进行改变

##五、GCD

(GCD)是Apple开发的⼀个多核编程的解决⽅方法。该⽅方法在Mac OS X 10.6首次推出,并随后被引入到了iOS4.0中。GCD是⼀个替代诸如NSThread, NSOperationQueue, NSInvocationOperation等技术的很高效和强大的技术。

###5.1 任务和队列

在GCD中,加入了两个很是重要的概念:任务和队列。

任务:

即操做,你想要干什么,说白了就是一段代码,在 GCD 中就是一个 Block,因此添加任务十分方便。

任务有两种执行方式: 同步执行 和 异步执行,

  • 同步(sync) 和 异步(async) 的主要区别在于会不会阻塞当前线程,直到 Block 中的任务执行完毕!

  • 若是是 同步(sync) 操做,它会阻塞当前线程并等待 Block 中的任务执行完毕,而后当前线程才会继续往下运行。

  • 若是是 异步(async)操做,当前线程会直接往下执行,它不会阻塞当前线程。

队列:

  • 用于存听任务。一共有两种队列, 串行队列DISPATCH_QUEUE_SERIAL 和 并行队列DISPATCH_QUEUE_CONCURRENT.

放到串行队列的任务,GCD 会 FIFO(先进先出) 地取出来一个,执行一个,而后取下一个,这样一个一个的执行。

放到并行队列的任务,GCD 也会 FIFO的取出来,但不一样的是,它取出来一个就会放到别的线程,而后再取出来一个又放到另外一个的线程。这样因为取的动做很快,忽略不计,看起来,全部的任务都是一块儿执行的。不过须要注意,GCD 会根据系统资源控制并行的数量,因此若是任务不少,它并不会让全部任务同时执行。

并行队列 中的任务根据同步或异步有不一样的执行方式。请看下表:

主队列 全局队列 自定义串行队列 自定义并行队列
同步 死锁(一直卡主) 在主线程中顺序执行 在主线程中顺序执行 在主线程中顺序执行
异步 在主线程中顺序执行 在其它线程中同时执行 在其它线程中顺序执行 在其它线程中同时执行

###5.2 建立队列

  • 主队列

这是一个特殊的串行队列。它用于刷新UI,任何须要刷新UI的工做都要在主队列执行,因此通常耗时的任务都要放到别的线程执行。

同步任务:dispatch_sync,会阻塞后面的任务,必需当前任务完成后才能执行下一个

主队列中不能执行同步操做,否则会死锁

异步执行:dispatch_async,不会阻塞后面的任务,任务会立刻返回下一个任务不须要等待,当这个任务完成后会通知主线程。 dispatch_get_main_queue() 获取主队列,主队列中的任务都会在主线程中执行, 是一个串行队列

dispatch_async(dispatch_get_main_queue(), ^{
       
        [self run_D];
    });
  • 全局队列

dispatch_get_global_queue() 全局队列,是一个并行队列,能够将队列中的任务放到不一样的线程中执行

任务执行的优先级
        DISPATCH_QUEUE_PRIORITY_HIGH 2  最高
        DISPATCH_QUEUE_PRIORITY_DEFAULT 0 中等(默认)
        DISPATCH_QUEUE_PRIORITY_LOW (-2) 低
        DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 后台执行
        
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
       
        [self run_A];
    });

若是在并行队列中同步执行任务,那么这些任务都会在主线程中按顺序执行,也就没有并发性了。

  • 自定义串行队列

自定义的串行队列中异步执行任务,队列会把任务放到一个新的线程中按顺序执行

dispatch_queue_t serialQueue = dispatch_queue_create("串行队列", DISPATCH_QUEUE_SERIAL);
    
    dispatch_async(serialQueue, ^{
        
        [self run_A];
    });
  • 自定义的并行队列

自定义的并行队列中异步执行任务,队列会把任务放到不一样的线程中执行

ispatch_queue_t concurrentlQueue = dispatch_queue_create("并行队列", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(concurrentlQueue, ^{
        
        [self run_A];
    });

将更新UI的操做放到主队列中

dispatch_async(dispatch_get_main_queue(), ^{
        
        self.imagView_1.image = image;
    
    });

###5.3 队列组

队列组能够将不少队列添加到一个组里,这样作的好处是,当这个组里全部的任务都执行完了,队列组会经过一个方法通知咱们。

建立一个队列组

dispatch_group_t group = dispatch_group_create();

建立队列

dispatch_queue_t queue = dispatch_queue_create("并行队列", DISPATCH_QUEUE_CONCURRENT);
    dispatch_queue_t global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

在group中异步执行任务,block中调用局部变量须要加__block

__block UIImage *image_1;
   
    dispatch_group_async(group, queue, ^{
       
        image_1 = [self downloadImage_one];
    });

当group中的任务都执行完后,就会发送一个通知notify调用这个方法

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
       
        self.imagView_1.image = image_1;        
    });

###5.4 线程安全

一次执行,block中的代码只能被执行一次,是线程保护的(同时只能一个线程访问)

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        
        [self run_B];
    });

单例通常用这个 线程安全的单例建立

+ (instancetype)shareUser_GCD {

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        user = [[self alloc] init];
    });
    
    return user;
}

+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    
    static User *user = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        user = [super allocWithZone:zone];
    });
    
    return user;
}

延迟执行

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        
        [self run_B];
    });

##六、NSOperation和NSOperationQueue

NSOperation 是苹果公司对 GCD 的封装,彻底面向对象,因此使用起来更好理解。 NSOperation 和 NSOperationQueue 分别对应 GCD 的 任务 和 队列 。操做步骤也很好理解:

1.将要执行的任务封装到一个 NSOperation 对象中。 2.将此任务添加到一个 NSOperationQueue 对象中。

直接执行会在主线程中顺序执行

NSInvocationOperation *opertaion_1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run_A) object:nil];

启动任务

[opertaion_1 start];

至少会有一个任务在主线程中执行,其余任务会被放到其余线程中执行

NSBlockOperation *opertion = [NSBlockOperation blockOperationWithBlock:^{
       
        [self run_A];
    }];
    
    [opertion addExecutionBlock:^{
        
        [self run_B];
    }];
    
    [opertion addExecutionBlock:^{
       
        [self run_C];
    }];
    
    [opertion start];

###6.1 NSInvocationOperation依赖关系

NSOperation 有一个很是实用的功能,那就是添加依赖。好比有 3 个任务:A: 从服务器上下载一张图片,B:给这张图片加个水印,C:把图片返回给服务器。这时就能够用到依赖

注意:

  • 不能添加相互依赖,会死锁,好比 A依赖B,B依赖A。
  • 可使用 removeDependency 来解除依赖关系。
  • 能够在不一样的队列之间依赖,反正就是这个依赖是添加到任务身上的,和队列不要紧。
NSInvocationOperation *opertaion_1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run_A) object:nil];
    NSInvocationOperation *opertaion_2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run_B) object:nil];
    NSInvocationOperation *opertaion_3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run_C) object:nil];
    
    // 添加依赖,opertaion_1 ————》opertaion_2 ————》opertaion_3
    
//    [opertaion_2 addDependency:opertaion_1];
//    [opertaion_3 addDependency:opertaion_2];

在这里就是opertaion_2会等opertaion_1执行完后再执行,后面以此类推

建立一个队列,默认就是并行队列

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

设置当前最大的执行数,能够控制是并行还串行,为1时就是串行

queue.maxConcurrentOperationCount = 1;

将任务添加到队列中任务就自动执行了

[queue addOperation:opertaion_1];
    [queue addOperation:opertaion_2];
    [queue addOperation:opertaion_3];
        [queue addOperationWithBlock:^{
    
            [self run_D];
        }];

添加多个任务,而且能够设置等待完成

[queue addOperations:@[opertaion_1,opertaion_2,opertaion_3] waitUntilFinished:YES];

取消全部的任务

[queue cancelAllOperations];

取消指定的任务

[opertaion_1 cancel];

获取当前的队列

[NSOperationQueue currentQueue];

获取主队列

[NSOperationQueue mainQueue];

###6.2 其它方法

以上就是一些主要方法, 下面还有一些经常使用方法:

NSOperation

  • BOOL executing; //判断任务是否正在执行
  • BOOL finished; //判断任务是否完成
  • void (^completionBlock)(void); //用来设置完成后须要执行的操做
  • (void)cancel; //取消任务
  • (void)waitUntilFinished; //阻塞当前线程直到此任务执行完 毕

NSOperationQueue

  • NSUInteger operationCount; //获取队列的任务数
  • (void)cancelAllOperations; //取消队列中全部的任务
  • (void)waitUntilAllOperationsAreFinished; //阻塞当前线程直到此队列中的全部任务执行完毕
  • [queue setSuspended:YES]; // 暂停queue
  • [queue setSuspended:NO]; // 继续queue

##7 GCD和NSOperation的区别:

一、GCD的底层是用C来实现的,NSOperation底层从ios4开始也是⽤的GCD来实 现的;(底层实现)

二、在NSOperationQueue中,咱们能够随时取消已经设定要准备执⾏的任务(固然,已经开始的任务就⽆法阻⽌了),⽽GCD无法中止已经加入queue的block(实际上是有的,但须要许多复杂的代码);(取消任务)

三、NSOperation可以⽅便地设置依赖关系,咱们可让⼀个Operation依赖于另⼀个Operation,这样的话尽管两个Operation处于同⼀个并行队列中,但前者会直到后者执行完毕后再执⾏;(依赖关系)

四、咱们能将KVO应⽤用在NSOperation中,能够监听一个Operation是否完成或取消,这样子能⽐GCD更加有效地掌控咱们执⾏的后台任务;(监放任务的执⾏状况)

五、在NSOperation中,咱们可以设置NSOperation的priority优先级,可以使同⼀个并行队列中的任务区分前后地执行,⽽在GCD中,咱们只能区分不一样任务队列的优先级,若是要区分block任务的优先级,也须要大量的复杂代码;(优先级) 六、咱们可以对NSOperation进行继承,在这之上添加成员变量与成员方法,提⾼整个代码的复⽤度,这⽐简单地将block任务排⼊执行队列更有自由度,可以在其之上添加更多自定制的功能。(代码复用)

Demo下载:

https://github.com/fuxinto/HfxDemo

相关文章
相关标签/搜索