GCD使用介绍

烈酒焚心 提交于 2020-04-20 07:16:33

一、概念

1、同步异步:
    a、同步 dispatch_sync():同步执行会等待当前任务执行完再向下执行
    b、异步  dispatch_async():不等待当前任务执行完,而是把任务提交后直接向下执行,不等待提交的任务

2、队列
    a、串行队列:提交到串行队列的任务会一个一个按顺序执行,上一个任务完成后、再执行下一个
    创建方式(2种一样):
                1、dispatch_queue_create("tongbu", NULL)
                2、dispatch_queue_create("tongbu", DISPATCH_QUEUE_SERIAL);
                         参数1:队列标签 参数2:队列类型(并发)
                3、dispatch_get_main_queue() 主队列也是串行队列
    b、并发队列:提交到并发队列的任务对并发执行,每个任务从提交的那一时刻起都会为它创建一个线
程,且无法确定哪一个任务先完成。
     创建方式(2种一样):
                1、dispatch_get_global_queue(0, 0)//获取系统的全局并发队列
                        其中,参数1 有以下可选
                         DISPATCH_QUEUE_PRIORITY_HIGH        //2
                        DISPATCH_QUEUE_PRIORITY_DEFAULT  //0 一般选这个即可
                        DISPATCH_QUEUE_PRIORITY_LOW        //-2
                        DISPATCH_QUEUE_PRIORITY_BACKGROUND // INT16_MIN  ( -32768)     
                2、dispatch_queue_create("tongbu", DISPATCH_QUEUE_CONCURRENT)//自己创建
                        参数1:队列标签 参数2:队列类型(并发)
特别提醒:dispatch_queue_create 每执行一次就会创建一个新对象,
                dispatch_queue_t queue1 =  dispatch_queue_create("tongbu", NULL);
                dispatch_queue_t queue2=  dispatch_queue_create("tongbu", NULL);
                此时queue1 != queue2

                所以使用时如果想要使用同一队列,一定要将其存储起来
!并发队列 执行时,并不一定会将添加到队列的任务全部一起执行,而是处理数量取决于系统的状态(CPU核数,负载。。)先执行前几个,比如一次性添加了10000个任务到并发队列,他可能由于负载原因只并发执行了第40个,当根据执行情况再并发后续任务

二、使用
    同步异步与队列结合使用

    连续添加十个任务
    组合1:同步 + 串行    

                无意义,连续添加十个任务,会加一个执行一个,第一个执行完,执行加入第二个
    组合2:异步 + 串行

                十个任务是按顺序添加,但是由于是异步添加,不会影响 "结束"的执行,同时,队列时串行队列,所以会挨个执行
    组合3:同步 + 并发

            无意义,以为他会等待添加的任务完成在继续下一步
    组合4:异步 + 并发

每个任务都会开启新线程去并发执行,相当于是个任务同时并发执行,且不会阻塞当前程序

三、函数方法
   1、 dispatch_barrier_async

    dispatch_queue_t queue = dispatch_queue_create("tongbu", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, readFile);
    dispatch_async(queue, readFile);
    //会等上面提交的任务完成后再去写,写完后才继续执行下面的提交任务,防止了读写竞争
    dispatch_barrier_async(queue, writeFile);
    
    dispatch_async(queue, readFile);
    dispatch_async(queue, readFile);

这样,多个read不会互相影响,提高了读性能,且不影响写操作(注意上面的是并发队列)

2、挂起/恢复
    注意:它的原理类似于引用计数,一个queue如果调用N次dispatch_suspend,那么要想恢复,就需要调用N次dispatch_resume才可以继续,而且dispatch_resume函数不能随意调用,只有当前队列被挂起后才可以对它使用该函数,否则会报错

dispatch_queue_t queue =  dispatch_queue_create("tongbu", NULL);
    dispatch_suspend(queue);//挂起队列
    dispatch_resume(queue);//恢复队列

3、定时器

    //dispatchQueue:该定时器任务在哪个队列执行
    //intervalInSeconds: 间隔时间
    //leewayInSeconds :设置为0即可 
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, <#dispatchQueue#>);
    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, <#intervalInSeconds#> * NSEC_PER_SEC, <#leewayInSeconds#> * NSEC_PER_SEC);
    dispatch_source_set_event_handler(timer, ^{
         //do something ...
    });
    //一次暂停对应一次开始,默认创建完成timer时是暂停的,原理见上一节
    //开始(不暂停时不能随意调用)
    dispatch_resume(timer);
    //暂停
    dispatch_suspend(timer);
    //销毁
    dispatch_source_cancel(_timer);

需要注意:要持有 timer 和 queue,不能让其销毁,否则定时器失效。

4、dispatch_semaphore_t 信号量
异步的向数组中添加对象会产生内存问题,需要进行锁处理,可以使用dispatch_semaphore_t信号量机制实现

- (void)test1 {
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);//并发队列
    NSMutableArray *array = [[NSMutableArray alloc] init];
    
    for (int i = 0; i < 10000; ++i) {
        dispatch_async(queue, ^{
            [array addObject:[NSNumber numberWithInteger:i]];//这里会产生内存问题
        });
    }
    NSLog(@"完成");
}

使用dispatch_semaphore_t

- (void)test2 {
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    NSMutableArray *array = [[NSMutableArray alloc] init];
    //信号量
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    for (int i = 0; i < 10000; ++i) {
        dispatch_async(queue, ^{
            
            //1、该函数会阻塞,等待直到semaphore计数 >= 1, 然后 内部将计数减1,然后该函数执行完返回
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            
            //2、执行到此处时, semaphore计数为0,代表没有线程修改array
            [array addObject:[NSNumber numberWithInteger:i]];
            
            //3、处理结束,修改信号量semaphore 加1,如果有dispatch_semaphore_wait函数等待,那么最先等待的那个线程就先执行
            dispatch_semaphore_signal(semaphore);
            
        });
    }
    NSLog(@"完成");
}

dispatch_semaphore_t,是一个持有计数的信号

    //创建信号 设置信号量总数
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(5);
    
    //释放一个信号,semaphore信号计数 +1
    dispatch_semaphore_signal(semaphore);
    
    //等待信号,当信号计数 >= 1 时就可以继续向下执行,同时 它将计数 进行减1 操作,,否则一直等待
    //参数2 为超时时间,
    //函数返回result: 0 代表success 信号计数 >=1, 非0 代表超时了,信号计数 <1
    long result = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

适用于有限资源的访问限制
5、Group

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_t group = dispatch_group_create();
    
    //添加任务到组
    dispatch_group_async(group, queue, ^{
        NSLog(@"任务1");
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"任务2");
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"任务3");
        [NSThread sleepForTimeInterval:5];
    });
    //任务1,2,3都执行完后才执行该任务
    dispatch_group_notify(group, queue, ^{
        NSLog(@"最后打印");
    });
    
    dispatch_time_t time =  dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC);
    //wait函数会阻塞当前线程,直到超时或者group提前执行完成,也可以将等待时间设置为DISPATCH_TIME_NOW,立即返回结果,不会阻塞
    // result: 0 代表已完成, 非0 未完成,已超时
    long result = dispatch_group_wait(group, time);
    NSLog(@"%ld", result);

6、dispatch_apply

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //按指定次数将block追加到queue
    //会阻塞当前线程,等待任务完成
    dispatch_apply(10, queue, ^(size_t i) {
        NSLog(@"%zu", i);
    });
    
    NSLog(@"最后打印");

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!