美文网首页
iOS知识点总结(5)- 多线程

iOS知识点总结(5)- 多线程

作者: 飞哥漂流记 | 来源:发表于2019-12-14 00:02 被阅读0次

    进程:一个程序的一次运行,在执行过程中拥有独立的内存单元,而多个线程共享一块内存

    线程:线程是指进程内的一个执行单元

    区别:

    (1)调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位

    (2)并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行

    (3)拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源.

    (4)系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。

    NSThread:

    直接操控线程对象,非常直观和方便 ,但是,线程的生命周期还是需要我们手动管理。

    创建并启动:

    NSThread  *thread = [[NSThread  alloc] initWithTarget:self  selector:@selector(run:) object:nil];;

    [thread  start];

    创建并自动启动

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

    其他操作方法:

    //取消线程

    - (void)cancel;

    //启动线程

    - (void)start;

    //判断某个线程的状态的属性

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

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

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

    //设置和获取线程名字

    -(void)setName:(NSString *)n;

    -(NSString *)name;

    //获取当前线程信息

    + (NSThread *)currentThread;

    //获取主线程信息

    + (NSThread *)mainThread;

    //使当前线程暂停一段时间,或者暂停到某个时刻

    + (void)sleepForTimeInterval:(NSTimeInterval)time;

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

    NSOperation和NSOperationQueue:

    NSOperation 是苹果公司对 GCD 的封装,完全面向对象,所以使用起来更好理解。 大家可以看到 NSOperation 和 NSOperationQueue 分别对应 GCD 的 任务 和 队列

    NSOperation 只是一个抽象类,所以不能封装任务。但它有 2 个子类用于封装任务。分别是:NSInvocationOperation 和 NSBlockOperation

    创建一个 Operation 后,需要调用 start 方法来启动任务,它会 默认在当前队列同步执行。当然你也可以在中途取消一个任务,只需要调用其 cancel 方法即可。

    //1.创建NSInvocationOperation对象

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

      //2.开始执行

      [operation start];

    //1.创建NSBlockOperation对象

      NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{

          NSLog(@"%@", [NSThread currentThread]);

      }];

      //2.开始任务

      [operation start];

    但是 NSBlockOperation 还有一个方法:addExecutionBlock: ,通过这个方法可以给 Operation 添加多个执行 Block。这样 Operation 中的任务 会并发执行,它会 在主线程和其它的多个线程 执行这些任务

    队列

    主队列:NSOperationQueue *queue = [NSOperationQueue mainQueue];

    其他队列: NSOperationQueue  *queue = [[NSOperationQueue alloc] init];

    //2.创建NSBlockOperation对象

    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{

        NSLog(@"%@", [NSThread currentThread]);

    }];

    //3.添加多个Block

    for (NSInteger i = 0; i < 5; i++) {

        [operation addExecutionBlock:^{

            NSLog(@"第%ld次:%@", i, [NSThread currentThread]);

        }];

    }

    //4.队列添加任务

    [queue addOperation:operation];

    添加依赖:

    [operation2 addDependency:operation1];//任务二依赖任务一

    [operation3 addDependency:operation2]; 

    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

    从其他线程回到主线程的方法:

    NSThread:[self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:NO];

    GCD://Objective-C

    dispatch_async(dispatch_get_main_queue(), ^{

    });

    NSOperationQueue://Objective-C

    [[NSOperationQueue mainQueue] addOperationWithBlock:^{

    }];

    GCD:

    1. GCD 简介

    是 Apple 开发的一个多核编程的较新的解决方法。它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统

    GCD 会自动管理线程的生命周期(创建线程、调度任务、销毁线程);

    程序员只需要告诉 GCD 想要执行什么任务,不需要编写任何线程管理代码。

    2. GCD 任务和队列

    GCD 中两个核心概念:『任务』 和 『队列』行任务有两种方式:『同步执行』 和 『异步执行』在 GCD 中有两种队列:『串行队列』 和 『并发队列』

    3. GCD 的使用步骤

    可以使用 dispatch_queue_create 方法来创建队列 

    第一个参数表示队列的唯一标识符,用于 DEBUG,可为空。队列的名称推荐使用应用程序 ID 这种逆序全程域名。

    第二个参数用来识别是串行队列还是并发队列。DISPATCH_QUEUE_SERIAL 表示串行队列,DISPATCH_QUEUE_CONCURRENT 表示并发队列

    // 串行队列的创建方法

    dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL);

    // 并发队列的创建方法

    dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);

    // 主队列的获取方法

    dispatch_queue_t queue = dispatch_get_main_queue();

    // 全局并发队列的获取方法

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    GCD 提供了同步执行任务的创建方法 dispatch_sync 和异步执行任务创建方法 dispatch_async。

    4. GCD 的基本使用(六种组合不同区别,队列嵌套情况区别,相互关系形象理解)

    参考 https://www.jianshu.com/p/2d57c72016c6

    5. GCD 线程间的通信

    /**

    * 线程间通信

    */

    - (void)communication {

        // 获取全局并发队列

        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

        // 获取主队列

        dispatch_queue_t mainQueue = dispatch_get_main_queue();

        dispatch_async(queue, ^{

            // 异步追加任务 1

            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作

            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程

            // 回到主线程

            dispatch_async(mainQueue, ^{

                // 追加在主线程中执行的任务

                [NSThread sleepForTimeInterval:2];              // 模拟耗时操作

                NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程

            });

        });

    }

    6. GCD 的其他方法

    GCD 栅栏方法:dispatch_barrier_async

    我们有时需要异步执行两组操作,而且第一组操作执行完之后,才能开始执行第二组操作。这样我们就需要一个相当于 栅栏 一

    样的一个方法将两组异步执行的操作组给分割起来,当然这里的操作组里可以包含一个或多个任务。这就需要用到

    dispatch_barrier_async 方法在两个操作组间形成栅栏。

    /**

    * 栅栏方法 dispatch_barrier_async

    */

    - (void)barrier {

        dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);

        dispatch_async(queue, ^{

            // 追加任务 1

            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作

            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程

        });

        dispatch_async(queue, ^{

            // 追加任务 2

            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作

            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程

        });

        dispatch_barrier_async(queue, ^{

            // 追加任务 barrier

            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作

            NSLog(@"barrier---%@",[NSThread currentThread]);// 打印当前线程

        });

        dispatch_async(queue, ^{

            // 追加任务 3

            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作

            NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程

        });

        dispatch_async(queue, ^{

            // 追加任务 4

            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作

            NSLog(@"4---%@",[NSThread currentThread]);      // 打印当前线程

        });

    }

    GCD 延时执行方法:dispatch_after:

    我们经常会遇到这样的需求:在指定时间(例如 3 秒)之后执行某个任务。可以用 GCD 的dispatch_after 方法来实现。

    需要注意的是:dispatch_after 方法并不是在指定时间之后才开始执行处理,而是在指定时间之后将任务追加到主队列中。严格来说,这个时间并不是绝对准确的,但想要大致延迟执行任务,dispatch_after 方法是很有效的。

    GCD 一次性代码(只执行一次):dispatch_once

    - (void)once {

        static dispatch_once_t onceToken;

        dispatch_once(&onceToken, ^{

            // 只执行 1 次的代码(这里面默认是线程安全的)

        });

    }

    GCD 快速迭代方法:dispatch_apply:

    - (void)apply {

        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

        NSLog(@"apply---begin");

        dispatch_apply(6, queue, ^(size_t index) {

            NSLog(@"%zd---%@",index, [NSThread currentThread]);

        });

        NSLog(@"apply---end");

    }

    GCD 队列组:dispatch_group:

    调用队列组的dispatch_group_async先把任务放到队列中,然后将队列放入队列组中。或者使用队列组的dispatch_group_enter、dispatch_group_leave组合来实现dispatch_group_async。

    调用队列组的dispatch_group_notify回到指定线程执行任务。或者使用dispatch_group_wait回到当前线程继续向下执行(会阻塞当前线程)。

    - (void)groupNotify {

        dispatch_group_t group =  dispatch_group_create();

        dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

            // 追加任务 1

            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作

            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程

        });

        dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

            // 追加任务 2

            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作

            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程

        });

        dispatch_group_notify(group, dispatch_get_main_queue(), ^{

            // 等前面的异步任务 1、任务 2 都执行完毕后,回到主线程执行下边任务

            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作

            NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程

            NSLog(@"group---end");

        });

    }

    dispatch_group_wait:

    暂停当前线程(阻塞当前线程),等待指定的 group 中的任务执行完成后,才会往下继续执行

    // 等待上面的任务全部完成后,会往下继续执行(会阻塞当前线程)

        dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

    dispatch_group_enter、dispatch_group_leave

    dispatch_group_enter 标志着一个任务追加到 group,执行一次,相当于 group 中未执行完毕任务数 +1

    dispatch_group_leave 标志着一个任务离开了 group,执行一次,相当于 group 中未执行完毕任务数 -1。

    当 group 中未执行完毕任务数为0的时候,才会使 dispatch_group_wait 解除阻塞,以及执行追加到 dispatch_group_notify 中的任务。

    /**

    * 队列组 dispatch_group_enter、dispatch_group_leave

    */

    - (void)groupEnterAndLeave {

        dispatch_group_t group = dispatch_group_create();

        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

        dispatch_group_enter(group);

        dispatch_async(queue, ^{

            // 追加任务 1

            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作

            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程

            dispatch_group_leave(group);

        });

        dispatch_group_enter(group);

        dispatch_async(queue, ^{

            // 追加任务 2

            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作

            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程

            dispatch_group_leave(group);

        });

        dispatch_group_notify(group, dispatch_get_main_queue(), ^{

            // 等前面的异步操作都执行完毕后,回到主线程.

            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作

        });

    }

    GCD 信号量:dispatch_semaphore:

    Dispatch Semaphore 提供了三个方法:

    dispatch_semaphore_create:创建一个 Semaphore 并初始化信号的总量

    dispatch_semaphore_signal:发送一个信号,让信号总量加 1

    dispatch_semaphore_wait:可以使总信号量减 1,信号总量小于 0 时就会一直等待(阻塞所在线程),否则就可以正常执行。

    /**

    * semaphore 线程同步

    */

    - (void)semaphoreSync {

        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

        __block int number = 0;

        dispatch_async(queue, ^{

            // 追加任务 1

            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作

            number = 100;

            dispatch_semaphore_signal(semaphore);

        });

        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

        NSLog(@"semaphore---end,number = %zd",number);

    }

    相关文章

      网友评论

          本文标题:iOS知识点总结(5)- 多线程

          本文链接:https://www.haomeiwen.com/subject/vnuqnctx.html