美文网首页
iOS多线程以及在项目中的使用

iOS多线程以及在项目中的使用

作者: 攻城狮GG | 来源:发表于2021-03-16 17:39 被阅读0次
    • pThread几乎不用,不用管
    • NSThread

    NSThread是对pThread的封装
    优点:
    1.实时性更高
    2.与RunLoop结合,提供更为灵活高效的线程管理方式
    缺点:
    1.创建线程代时,需要同时占用应用和内核的内2.存空间(GCD只占用内核的内存空间)
    编写线程相关代码相对繁杂

    线程的创建方式

        [NSThread detachNewThreadWithBlock:^{
            NSLog(@"NSThread");
        }];
        // 2.
        [NSThread detachNewThreadSelector:@selector(dosomething:) toTarget:self withObject:data];
        // 3.
        NSThread * thread = [[NSThread alloc]initWithTarget:self selector:@selector(dosomething:) object:data];
        thread.name = @"thread1";
        [thread start];
    
    • GCD

    项目中使用dispatch_async处理证件信息修改对OCR识别到大图片压缩耗时操作,压缩完成后去请求接口,JJ的做任务下载用了dispatch_group_t,还有JJ的检测最优线路也用了dispatch_group_t等

    dispatch_sync同步
    dispatch_async异步任务派发
    dispatch_queue_t串行队列与并发队列
    dispatch_once_t只执行一次
    dispatch_after延后执行
    dispatch_group_t组调度
    dispatch_barrier_(a)sync栅栏
    dispatch_semaphore信号量

    dispatch_semaphore 是信号量,但当信号总量设为 1 时也可以当作锁来。在没有等待情况出现时,它的性能比 pthread_mutex(自旋锁) 还要高,但一旦有等待情况出现时,性能就会下降许多。相对于 OSSpinLock 来说,它的优势在于等待时不会消耗 CPU 资源。对磁盘缓存来说,它比较合适。自旋锁更适合内存缓存,虽然占用一些CPU,但是快

    //可设置最大并发数,目前最大并发数为5,设置最大并发数还是建议用NSOperation
    dispatch_semaphore_t sem = dispatch_semaphore_create(5);
    //最大并发为1时也可以当锁使用
    dispatch_semaphore_t sem = dispatch_semaphore_create(1);
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
    执行耗时操作};
    dispatch_async(dispatch_get_main_queue(), ^{
    回到主线程进行UI刷新操作
    };
    

    GCD中dispatch_queue大致可以分为三类
    全局的并行的queue

    主线程的串行的queue
    自定义的queue
    全局的queue和主线程的queue结合使用(上边提到的)就是我们平常最常用的一种用法,在异步线程中执行耗时操作,然后在UI线程执行刷新操作。
    全局的queue我们可以通过dispatch_get_global_queue(0, 0)直接获取,这里有两个参数,第一个表示线程执行的优先级(第二个参数是预留参数暂时没用)
    第一个参数有以下几个状态
    DISPATCH_QUEUE_PRIORITY_HIGH 2
    -》DISPATCH_QUEUE_PRIORITY_DEFAULT 0
    -》DISPATCH_QUEUE_PRIORITY_LOW (-2)
    -》DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN,具体打印结果看下面内容
    下面我们设置第一个参数默认值为0

        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"start task 1");
            [NSThread sleepForTimeInterval:3];
            NSLog(@"end task 1");
        });
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"start task 2");
            [NSThread sleepForTimeInterval:3];
            NSLog(@"end task 2");
        });
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"start task 3");
            [NSThread sleepForTimeInterval:3];
            NSLog(@"end task 3");
        });
    
    输出结果
    2021-03-16 15:49:52.344146+0800 TestDemo[54759:1269344] start task 2
    2021-03-16 15:49:52.344151+0800 TestDemo[54759:1269347] start task 3
    2021-03-16 15:49:52.344146+0800 TestDemo[54759:1269346] start task 1
    
    2021-03-16 15:49:55.348151+0800 TestDemo[54759:1269346] end task 1
    2021-03-16 15:49:55.348151+0800 TestDemo[54759:1269347] end task 3
    2021-03-16 15:49:55.348218+0800 TestDemo[54759:1269344] end task 2
    

    下面设置优先级

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
            NSLog(@"start task 0");
            [NSThread sleepForTimeInterval:3];
            NSLog(@"end task 0");
        });
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
            NSLog(@"start task 1");
            [NSThread sleepForTimeInterval:3];
            NSLog(@"end task 1");
        });
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
            NSLog(@"start task 2");
            [NSThread sleepForTimeInterval:3];
            NSLog(@"end task 2");
        });
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSLog(@"start task 3");
            [NSThread sleepForTimeInterval:3];
            NSLog(@"end task 3");
        });
    021-03-16 16:04:20.881211+0800 TestDemo[54815:1279607] start task 2
    2021-03-16 16:04:20.881211+0800 TestDemo[54815:1279609] start task 3
    2021-03-16 16:04:20.922642+0800 TestDemo[54815:1279608] start task 1
    2021-03-16 16:04:21.037538+0800 TestDemo[54815:1279606] start task 0
    2021-03-16 16:04:23.882068+0800 TestDemo[54815:1279609] end task 3
    2021-03-16 16:04:23.882066+0800 TestDemo[54815:1279607] end task 2
    2021-03-16 16:04:24.057318+0800 TestDemo[54815:1279608] end task 1
    2021-03-16 16:04:24.133597+0800 TestDemo[54815:1279606] end task 0
    

    串行队列和并发队列
    dispatch_queue_create(const char *label, dispatch_queue_attr_t attr),这个方法同样也有两个参数,第一个参数是确定唯一queue的一个标识,第二个参数创建queue的类型
    DISPATCH_QUEUE_SERIAL(串行)
    DISPATCH_QUEUE_CONCURRENT(并发)

    dispatch_queue_t myQueue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_SERIAL(串行)/DISPATCH_QUEUE_CONCURRENT(并发));
        dispatch_async(myQueue, ^{
            NSLog(@"start task 1");
            [NSThread sleepForTimeInterval:3];
            NSLog(@"end task 1");
        });
        dispatch_async(myQueue, ^{
            NSLog(@"start task 2");
            [NSThread sleepForTimeInterval:3];
            NSLog(@"end task 2");
        });
        dispatch_async(myQueue, ^{
            NSLog(@"start task 3");
            [NSThread sleepForTimeInterval:3];
            NSLog(@"end task 3");
        });
    
    串行队列输出结果
    2021-03-16 16:28:15.643198+0800 TestDemo[55024:1292810] start task 1
    2021-03-16 16:28:18.643548+0800 TestDemo[55024:1292810] end task 1
    2021-03-16 16:28:18.643854+0800 TestDemo[55024:1292810] start task 2
    2021-03-16 16:28:21.648152+0800 TestDemo[55024:1292810] end task 2
    2021-03-16 16:28:21.648631+0800 TestDemo[55024:1292810] start task 3
    2021-03-16 16:28:24.653826+0800 TestDemo[55024:1292810] end task 3
    
    并发队列输出结果
    2021-03-16 16:33:54.921235+0800 TestDemo[55071:1297272] start task 2
    2021-03-16 16:33:54.921235+0800 TestDemo[55071:1297270] start task 1
    2021-03-16 16:33:54.921243+0800 TestDemo[55071:1297271] start task 3
    2021-03-16 16:33:57.923428+0800 TestDemo[55071:1297272] end task 2
    2021-03-16 16:33:57.923442+0800 TestDemo[55071:1297271] end task 3
    2021-03-16 16:33:57.923442+0800 TestDemo[55071:1297270] end task 1
    

    dipatch_group(调度组)的使用,项目使用JJ的多章节任务下载,等所有任务下载成功后弹窗提示已完成,还有就是检测最优线路后刷新UI
    重点:
    dispatch_group_enter()(入组)
    dispatch_group_leave()(出组)

    - (void)test{
      dispatch_group_t group = dispatch_group_create();
    
        dispatch_group_enter(group);
        [self sendRequest1:^{
            dispatch_group_leave(group);
        }];
    
        dispatch_group_enter(group);
        [self sendRequest2:^{
            dispatch_group_leave(group);
        }];
    
        dispatch_group_notify(group, dispatch_queue_create(0, 0), ^{
            NSLog(@"task over");
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"refresh ui");
            });
        });
    }
    - (void)sendRequest1:(void(^)())block {
    //异步请求,请求结果后block回调
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"start task 1");
        [NSThread sleepForTimeInterval:3];
        NSLog(@"end task 1");
        dispatch_async(dispatch_get_main_queue(), ^{
            if (block) {
                block();
            }
        });
    });
    }
    - (void)sendRequest2:(void(^)())block {
    //异步请求,请求结果后block回调
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"start task 2");
        [NSThread sleepForTimeInterval:3];
        NSLog(@"end task 2");
        dispatch_async(dispatch_get_main_queue(), ^{
            if (block) {
                block();
            }
        });
    });
    }
    输出结果
    2021-03-16 16:53:52.788430+0800 TestDemo[55332:1314700] start task 2
    2021-03-16 16:53:52.788430+0800 TestDemo[55332:1314695] start task 1
    2021-03-16 16:53:55.789766+0800 TestDemo[55332:1314700] end task 2
    2021-03-16 16:53:55.789766+0800 TestDemo[55332:1314695] end task 1
    2021-03-16 16:53:55.790373+0800 TestDemo[55332:1314695] task over
    2021-03-16 16:53:55.790798+0800 TestDemo[55332:1314538] refresh ui
    

    dispatch_once_t一次执行,常用来实现单例模式,这里以单例模式实现的模板代码为例展示dispatch_once_t的用法,其中的实例化语句只会被执行一次:

    + (instancetype *)sharedInstance {
        static dispatch_once_t once = 0;
        static id sharedInstance = nil;
        dispatch_once(&once, ^{
            // 只实例化一次
            sharedInstance = [[self alloc] init];
        });
        return sharedInstance;
    }
    

    dispatch_barrier_(a)sync 栅栏函数
    通过Dispatch_barrier_async添加的操作会暂时阻塞当前队列,即等待前面的并发操作都完成后执行该阻塞操作,待其完成后后面的并发操作才可继续。可以将其比喻为一座霸道的独木桥,是并发队列中的一个并发障碍点,临时阻塞并独占。
    可见使用Dispatch_barrier_async可以实现类似dispatch_group_t组调度的效果,同时主要的作用是避免数据竞争,高效访问数据。

    ⚠️官方说明大意:在使用栅栏函数时.使用自定义队列才有意义,如果用的是串行队列或者系统提供的全局并发队列,这个栅栏函数的作用等同于一个同步函数的作用

    ☆⚠️关于dispatch_barrier_(a)sync区别
    个人理解:dispatch_barrier_sync 需要等待栅栏执行完才会执行栅栏后面的任务,而dispatch_barrier_async 无需等待栅栏执行完,会继续往下走(保留在队列里)

    /* 创建并发队列 */
        dispatch_queue_t concurrentQueue = dispatch_queue_create("test.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
        /* 添加两个并发操作A和B,即A和B会并发执行 */
        dispatch_async(concurrentQueue, ^(){
            NSLog(@"OperationA");
        });
        dispatch_async(concurrentQueue, ^(){
            NSLog(@"OperationB");
        });
        /* 添加barrier障碍操作,会等待前面的并发操作结束,并暂时阻塞后面的并发操作直到其完成 */
        dispatch_barrier_async(concurrentQueue, ^(){
            NSLog(@"OperationBarrier!");
        });
        NSLog(@"验证async和sync的区别--现在是async");
        /* 继续添加并发操作C和D,要等待barrier障碍操作结束才能开始 */
        dispatch_async(concurrentQueue, ^(){
            NSLog(@"OperationC");
        });
        dispatch_async(concurrentQueue, ^(){
            NSLog(@"OperationD");
        });
    ⚠️ dispatch_barrier_async输出结果
    2021-03-16 17:33:09.429076+0800 TestDemo[55672:1343472] OperationB
    2021-03-16 17:33:09.429084+0800 TestDemo[55672:1343332] 验证async和sync的区别--现在是async
    2021-03-16 17:33:09.429090+0800 TestDemo[55672:1343476] OperationA
    2021-03-16 17:33:09.429235+0800 TestDemo[55672:1343476] OperationBarrier!
    2021-03-16 17:33:09.429327+0800 TestDemo[55672:1343476] OperationC
    2021-03-16 17:33:09.429329+0800 TestDemo[55672:1343472] OperationD
    
    ⚠️dispatch_barrier_sync输出结果
    2021-03-16 17:34:36.483220+0800 TestDemo[55690:1345240] OperationB
    2021-03-16 17:34:36.483245+0800 TestDemo[55690:1345246] OperationA
    2021-03-16 17:34:36.483487+0800 TestDemo[55690:1345114] OperationBarrier!
    2021-03-16 17:34:36.483578+0800 TestDemo[55690:1345114] 验证async和sync的区别--现在是sync
    2021-03-16 17:34:36.483675+0800 TestDemo[55690:1345242] OperationD
    2021-03-16 17:34:36.483681+0800 TestDemo[55690:1345246] OperationC
    

    dispatch_after
    通过该函数可以让要提交的任务在从提交开始后的指定时间后执行,也就是定时延迟执行提交的任务,使用方法很简单:

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                //延迟执行
            });
    
    • NSOperation 和 NSOperationQueue

    项目中的使用:例如微信分享图片时,等图片下载完后再分享,用的NSOperationQueue
    当 NSOperation 支持了 cancel 操作时,NSOperationQueue 可以使用 cancelAllOperatoins 来对所有的 operation 执行 cancel 操作。
    不过 cancel 的效果还是取决于 NSOperation 中代码是怎么写的。(比如 对于数据库的某些操作线程来说,cancel 可能会意味着 你需要把数据恢复到最原始的状态。)

    maxConcurrentOperationCount设置最大并发数
    默认的最大并发 operation 数量是由系统当前的运行情况决定的(来源),我们也可以强制指定一个固定的并发数量。

    GCD 与 NSOperation 的对比,面试中会经常问,因为这两个都很强大,我们也都经常在用

    • NSOperationQueue 是基于 GCD 的更高层的封装(从 OS X 10.10 开始可以通过设置 underlyingQueue 来把 operation 放到已有的 dispatch queue 中。)
    • 从易用性角度,GCD 由于采用 C 风格的 API,在调用上比使用面向对象风格的 NSOperation 要简单一些。
    • 从对任务的控制性来说,NSOperation 显著得好于 GCD,和 GCD 相比支持了 Cancel 操作(注:在 iOS8 中 GCD 引入了 dispatch_block_cancel 和 dispatch_block_testcancel,也可以支持 Cancel 操作了),支持任务之间的依赖关系,支持同一个队列中任务的优先级设置,同时还可以通过 KVO 来监控任务的执行情况。(这些通过 GCD 也可以实现,不过需要很多代码,使用 NSOperation 显得方便了很多。)
    • 从第三方库的角度,知名的第三方库如 AFNetworking 和 SDWebImage 背后都是使用 NSOperation,也从另一方面说明对于需要复杂并发控制的需求,NSOperation 可能是更好的选择,但也并不绝对,各有利弊,这只是个人理解。

    NSOperation的使用
    maxConcurrentOperationCount设置对大并发数
    ⚠️切勿添加循环依赖

    - (void)test
    {
        NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"11111111%@", [NSThread currentThread]);
        }];
          
        NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"22222%@", [NSThread currentThread]);
        }];
          
        NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
            sleep(3);
            NSLog(@"333333 %@", [NSThread currentThread]);
        }];
          
        NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"44444444%@", [NSThread currentThread]);
        }];
          
        // 指定操作之间的”依赖“关系,某一个操作的执行,必须等待另一个操作完成才会开始
        // 依赖关系是可以跨队列指定的
        [op2 addDependency:op1];
        [op3 addDependency:op2];
        [op4 addDependency:op3];
        // *** 添加依赖的时候,注意不要出现循环依赖
    //    [op3 addDependency:op4];
          
        [self.queue addOperation:op1];
        [self.queue addOperation:op2];
        [self.queue addOperation:op3];
        // 主队列更新UI
        [[NSOperationQueue mainQueue] addOperation:op4];
    }
    - (NSOperationQueue *)queue
    {
        if (!_queue) _queue = [[NSOperationQueue alloc] init];
        return _queue;
    }
    

    相关文章

      网友评论

          本文标题:iOS多线程以及在项目中的使用

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