OC底层原理

多线程GCD

2020-04-21  本文已影响0人  只写Bug程序猿

概念

GCD全称Grand Central Dispatch纯c语言,提供了非常多强大的函数
GCD是苹果公司为多核的并行运算提出的解决方案
GCD会自动利用更多的CPU内核(双核,四核)
GCD会自动管理线程的生命周期(创建线程,调度线程,销毁线程)
只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码

执行任务的函数有同步函数和异步函数

同步函数

同步 dispatch_sync特点:

异步函数

异步 dispatch_async特点:

队列也分为两种,串行和并行队列

队列是一种数据结构,遵循FIFO(first in first out)原则
串行队列,一次只能执行一个任务,并且任务是一个一个的按照顺序来执行的
并行队列,一个执行多个任务,任务的执行完成时间不可控制,跟任务复杂度和cpu调度有关系
下边来一张图进行一个对比


函数和队列组合进行任务执行的几种情况
1 同步函数+串行队列
2 同步函数+并发队列
3 异步函数+ 串行队列
4 异步函数+并行队列
主队列
全局队列

函数和队列的应用

来先看一道微博面试题

- (void)wbinterDemo{
    dispatch_queue_t queue = dispatch_queue_create("com.lg.cooci.cn", DISPATCH_QUEUE_CONCURRENT);    
    
    dispatch_async(queue, ^{
        // sleep(2);
        NSLog(@"1");
    });
    dispatch_async(queue, ^{
        NSLog(@"2");
    });
    // 堵塞 - 护犊子
    dispatch_sync(queue, ^{
        NSLog(@"3");
    });
    // **********************
    NSLog(@"0");

    dispatch_async(queue, ^{
        NSLog(@"7");
    });
    dispatch_async(queue, ^{
        NSLog(@"8");
    });
    dispatch_async(queue, ^{
        NSLog(@"9");
    });
    // A: 1230789
    // B: 1237890
    // C: 3120798
    // D: 2137890
    
}

分析:

栅栏函数dispatch_barrier

现在一个需求,任务1跟任务2,等1,2执行完之后打印.这个时候我们就可以利用栅栏函数来实现

dispatch_queue_t concurrentQueue = dispatch_queue_create("aaaa", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQueue, ^{
   NSLog(@"任务1");
 });
   
dispatch_async(concurrentQueue, ^{
   NSLog(@"任务2");
});
//阻塞队列
dispatch_barrier_async(concurrentQueue, ^{
    NSLog(@"任务1,2执行完了");
});
 NSLog(@"************起来干!!");

问题 dispatch_barrier_async换成dispatch_barrier_sync会有什么不一样呢
dispatch_barrier_sync不光会阻塞队列还会阻塞线程,NSLog(@"************起来干!!");打印时机会提前

可变数组线程不安全,解决办法
dispatch_queue_t concurrentQueue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT);
    //    dispatch_queue_t concurrentQueue = dispatch_get_global_queue(0, 0);

    // signal -- 线程BUG
    for (int i = 0; I<10000; i++) {
        dispatch_async(concurrentQueue, ^{
            NSString *imageName = [NSString stringWithFormat:@"%d.jpg", (i % 10)];
            NSURL *url = [[NSBundle mainBundle] URLForResource:imageName withExtension:nil];
            NSData *data = [NSData dataWithContentsOfURL:url];
            UIImage *image = [UIImage imageWithData:data];
            
          //  dispatch_barrier_async(concurrentQueue, ^{
                [self.mArray addObject:image];
          //  });
        });
    }

报错内容

Thread 8: signal SIGABRT
malloc: *** error for object 0x7ff105601a00: pointer being freed was not allocated
malloc: *** set a breakpoint in malloc_error_break to debug

运行这段代码会发现当循环次数较大时可能会直接崩溃,崩溃原因是,多个线程同时访问数组往数组里边加元素,此时数组是不安全的,如何解决这个问题

//可以加锁
@synchronized (self) {
         [self.mArray addObject:image];
  }
//还可以加一个栅栏函数
 dispatch_barrier_async(concurrentQueue, ^{
     [self.mArray addObject:image];
 });

坑点队列换成全局并发队列dispatch_get_global_queue,发现也会崩溃
全局并发队列不止你一个人在使用,系统也在使用,所以全局队列不能堵塞

注意 :
  1. 栅栏函数一定要使用自定义的队列
  2. 栅栏函数使用的队列一定要是同一个队列(afn请求使用栅栏发现没有效果,因为afn内部有自己的一个队列.不是同一个队列)
  3. dispatch_barrier_sync会阻塞线程一定要注意

调度组 dispatch_group

dispatch_group_notify

dispatch_group_notify当组中的任务执行完成会来到这个函数

 //创建调度组
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_queue_t queue1 = dispatch_queue_create("com.test", DISPATCH_QUEUE_CONCURRENT);
 dispatch_group_async(group, queue, ^{
       NSLog(@"来了1");
    });
    
    dispatch_group_async(group, queue1, ^{
        sleep(2);
       NSLog(@"来了2");
    });
 dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"执行完了");
 });

Group进组出组另一种写法

   dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_group_t group = dispatch_group_create();
    
    dispatch_group_enter(group);
    
    // dispatch_group_async -- 下节课的源码分析 --封装意思
    dispatch_async(queue, ^{
        NSLog(@"第一个走完了");
        dispatch_group_leave(group);
    });
    
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@"第二个走完了");
    });
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"所有任务完成,可以更新UI");
    });
dispatch_group_wait
     dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout)
     
     dispatch_time_t timeout:参数用来指定等待的时间
     
     这里的等待表示,一旦调用dispatch_group_wait函数,该函数就处理调用的状态而不返回值,只有当函数的currentThread停止,或到达wait函数指定的等待的时间,或Dispatch Group中的操作全部执行完毕之前,执行该函数的线程停止.     
     当指定timeout为DISPATCH_TIME_FOREVER时就意味着永久等待
     当指定timeout为DISPATCH_TIME_NOW时就意味不用任何等待即可判定属于Dispatch Group的处理是否全部执行结束
     如果dispatch_group_wait函数返回值不为0,就意味着虽然经过了指定的时间,但Dispatch Group中的操作并未全部执行完毕
     
     如果dispatch_group_wait函数返回值为0,就意味着Dispatch Group中的操作全部执行完毕
  long timeout = dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW,1 * NSEC_PER_SEC));
    if (timeout == 0) {
        NSLog(@"回来了");
    }else{
        NSLog(@"等待中 -- 转菊花");
    }
    
    
    __block UIImage *newImage = nil;
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"所有任务完成,可以更新UI");
    });

信号量dispatch_semaphore_t

信号量可以控制线程之间的执行顺序和依赖关系,从而达到线程同步的目的

  1. 停车场进车,开始时车位肯定是充足的所以此时value肯定不能为0,车进的时候看value是否为0,为0就在这等待,当一辆车出去的时候发送一个消息信号量+1,这时等待的车辆收到消息进入
  2. 比如我要买一个煎饼果子,刚开始老板还没做所以value为0 ,当顾客要买的时候先等待老板制作,制作好了信号量+1,顾客买走信号量-1,下一个顾客来买时继续等待制作

使用的时候一定要注意死锁问题
使用示例

 dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    // 创建信号量
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    
    // 添加任务1
    dispatch_async(globalQueue, ^{
        sleep(1);
        NSLog(@"task 1");
        // 任务1执行完毕,信号量 = -1 + 1 = 0,继续执行wait之后的代码
        dispatch_semaphore_signal(semaphore);
    });
    
    NSLog(@"wait task 1...");
    // 信号量 = 0 - 1 < 0,线程被阻塞,等待任务1执行完毕
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    
    // 添加任务2
    dispatch_async(globalQueue, ^{
        NSLog(@"task 2");
        dispatch_semaphore_signal(semaphore);
    });
    
    NSLog(@"wait task 2...");
    // 阻塞线程,等待任务2结束
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    
    // 所有任务结束
    NSLog(@"All tasks done!");
}
上一篇 下一篇

猜你喜欢

热点阅读