GCD笔记

  苹果官方说明,GCD是异步执行任务的技术之一,一般将应用程序中记述的线程管理用的代码在系统级中实现。开发者只需要定义想执行的任务并追加到适当的Dispatch Queue中,GCD就能生成必要的线程并计划执行任务。由于线程管理是作为系统的一部分来实现的,因此可以统一管理,也可以执行任务。如下:


    //串形队列 第二个参数最好为 NULL 参考源码

  dispatch_queue_t serialQueue = dispatch_queue_create("com.example.gcd.mySerialQueue", NULL);

     //并行队列 第二个参数最好为 DISPATCH_QUEUE_CONCURRENT

    dispatch_queue_t concurrentQueue = dispatch_queue_create("com.example.gcd.myCurrentQueue", DISPATCH_QUEUE_CONCURRENT);

  dispatch_async(dispatch_get_global_queue(0, 0), ^{
        // 长时间处理  如:数据库访问
        
        dispatch_async(dispatch_get_main_queue(), ^{
           //主线程更新
        });
    });

其实只有第一行代码表示让处理在后台执行。也只有一行代码让处理在主线程中执行。

在GCD之前,Cocoa框架也提供NSObject类来实现同样的效果:

    //执行后台线程
    [self performSelectorInBackground:@selector(doWork) withObject:nil];
- (void)doWork{
    // 长时间处理  如:数据库访问
    // ...........................
    //处理结束  ,主线城处理结果
    [self performSelectorOnMainThread:@selector(doneWork) withObject:nil waitUntilDone:NO];
}

- (void)doneWork{
    //主线程执行    如用户界面更新
}

这个方法的确要比NSThread提供的简单,但是相比于GCD还是繁琐了。在多线程编程中,由于CPU一次只能执行一个命令,GCD的优势就更加明显。

 

------Dispatch Queue的种类

    //获取主线程 属于serial queue
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    //高优先级获取方法 属于concurrent queue
    dispatch_queue_t globalDispatchQueueHigh = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
    //默认先级获取方法 属于concurrent queue
    dispatch_queue_t globalDispatchQueueDefault = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //低优先级获取方法 属于concurrent queue
    dispatch_queue_t globalDispatchQueueLow = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
    //后台优先级获取方法 属于concurrent queue
    dispatch_queue_t globalDispatchQueueBg = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);--

-----指定优先级 ,如果在多个serial dispatch queue中用此函数制定目标为某一个serial dispatch queue,那么原本可以并发执行的多个serial dispatch queue,在目标serial dispatch queue上只能同时执行一个处理。

   //指定变更优先级 第一个参数如果是系统提供的如main dispatch queue 和  global dispatch queue 均不可指定
    //第一个参数指定变更优先级   第二个参数指定与要使用的执行的相同优先级
    dispatch_set_target_queue(globalDispatchQueueHigh, globalDispatchQueueLow);

-----Dispatch Group

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_t groupQueue = dispatch_group_create();
    //这五个block并发执行
    dispatch_group_async(groupQueue, queue, ^{NSLog(@"block0");});
    dispatch_group_async(groupQueue, queue, ^{NSLog(@"block1");});
    dispatch_group_async(groupQueue, queue, ^{NSLog(@"block2");});
    dispatch_group_async(groupQueue, queue, ^{NSLog(@"block3");});
    dispatch_group_async(groupQueue, queue, ^{NSLog(@"block4");});
    //结束处理 一定是最后执行的
    dispatch_group_notify(groupQueue, queue, ^{NSLog(@"done");});

dispatch_group中也可以使用dispatch_wait函数等待全部处理执行结束

// DISPATCH_TIME_FOREVER 永久等待,group未结束就会一直等待
    dispatch_group_wait(groupQueue, DISPATCH_TIME_FOREVER);
    //如:
    dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC);
    long result =  dispatch_group_wait(groupQueue, time);
    if (result == 0) { //如果不为0,就说明虽然过了等待时间,但是某一个group还在处理执行中。
        //属于dispatch group的全部处理执行结束
        NSLog(@"属于dispatch group的全部处理执行结束");
    }else{
        //属于dispatch group的某一个处理还在执行中
        NSLog(@"属于dispatch group的某一个处理还在执行中"); }

还有经常碰到的等待多个异步请求执行完毕方法如下:

    dispatch_group_t serviceGroup = dispatch_group_create();
    //开始第一个请求
    //进入组
    dispatch_group_enter(serviceGroup);
    [self.configService startWithCompletion:^(ConfigResponse *resul ts, NSError* error){
        configError = error;
        //离开组
        dispatch_group_leave(serviceGroup);
    }];
    //开始第二个请求
    //进入组
    dispatch_group_enter(serviceGroup);
    [self.preferenceService startWithCompletion:^(PreferenceRespons e *results, NSError* error){
    
        //离开组
        preferenceError = error;
        dispatch_group_leave(serviceGroup);
    }];
    //当小组里的任务都清空以后 通知主线程
    dispatch_group_notify(serviceGroup,dispatch_get_main_queue(),^{
        // Assess any errors
        NSError *overallError = nil;
        if (configError || preferenceError) {
            // Either make a new error or assign one of them to the
            overall error
            overallError = configError ?: preferenceError;
        }
        // Now call the final completion block
        completion(overallError);
    });

 

 ----dispatch_barrier_async在访问数据库或文件时,使用serial dispatch queue 可避免数据竞争。

    dispatch_queue_t queue = dispatch_queue_create("com.example.gcd.barrier", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{NSLog(@"block0_for_reading");});
    dispatch_async(queue, ^{NSLog(@"block1_for_reading");});
    dispatch_async(queue, ^{NSLog(@"block2_for_reading");});
    dispatch_async(queue, ^{NSLog(@"block3_for_reading");});
    //需要写入处理 -- 当前面执行完再执行dispatch_barrier中的处理,在执行后面的操作
    dispatch_barrier_async(queue, ^{NSLog(@"blockBarrier_for_writing");});
    dispatch_async(queue, ^{NSLog(@"block4_for_reading");});
    dispatch_async(queue, ^{NSLog(@"block5_for_reading");});
    dispatch_async(queue, ^{NSLog(@"block6_for_reading");});
    dispatch_async(queue, ^{NSLog(@"block7_for_reading");});
    dispatch_async(queue, ^{NSLog(@"block8_for_reading");});

         

----dispatch_apply    是dispatch_async和dispatch_group的关联API。

按指定的次数将指定的block追加到指定的dispatch_queue中,并等待全部执行结束。

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_apply(10, queue, ^(size_t index) {
        NSLog(@"%zu",index);
    });
    NSLog(@"done");

          

有这样的功能,我们就可以很安全的去循环数组进行元素处理(注:不是按顺序执行)。

    NSArray *array = @[@"00",@"11",@"22",@"33",@"44",@"55",@"66",@"77",@"88",@"99",@"xx"];
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_apply(array.count, queue, ^(size_t index) {
        NSLog(@"%zu-%@",index,array[index]);
    });
    NSLog(@"done");

另外,由于dispatch_apply 和 diapatch_sync 函数相同。会等待处理执行结束,因此推荐在dispatch_async函数中非同步的执行dispatch_apply函数

    NSArray *array = @[@"00",@"11",@"22",@"33",@"44",@"55",@"66",@"77",@"88",@"99",@"xx"];
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //在global dispatch queue中非同步执行
    dispatch_async(queue, ^{
       //等待dispatch_apply函数中全部处理执行结束
        dispatch_apply(array.count, queue, ^(size_t index) {
           //并列处理包含在NSArray对象的全部对象
            NSLog(@"%zu-%@",index,[array objectAtIndex:index]);
        });
        
        //dispatch_apply函数全部处理执行结束
        dispatch_async(dispatch_get_main_queue(), ^{
           //主线程中更新UI
            NSLog(@"主线程中更新UI");
        });
    });

----dispatch_suspend 挂起停止执行, dispatch_resume恢复执行

----dispatch_semaphore 是持有计数的信号,此计数是变线程编程中计数类型信号。类似于过马路的手旗,可以通过举起手旗,不可通过放下手旗。dispatch_semaphore,计数为0等待,计数大于或等于1时,减去1而不等待。

//创建dispatch_semaphore 计数值初始化为1
    dispatch_semaphore_t  semaphore = dispatch_semaphore_create(1);
    dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC);
    long result = dispatch_semaphore_wait(semaphore, time);
    if (result == 0) {//返回为0的时候可以安全的执行可以进行排他控制的处理
        /*
         由于dispatch_semaphore的计数值达到大于等于1
         或者在待机中的指定时间内
         dispatch_semaphore的计数值达到大于等于1
         所以dispatch_semaphore的计数值减去1.
         
         可执行需要进行排他控制处理
         */
    }else{
        /*
         由于dispatch semaphore的计数值为0
         因此在达到指定时间为止待机
         */
    }

实际使用:

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //创建dispatch_semaphore 计数值初始化为1
    //保证访问NSMutableArray类对象的线程同时只能有一个
    dispatch_semaphore_t  semaphore = dispatch_semaphore_create(1);
    NSMutableArray *array = [[NSMutableArray alloc]init];
    
    for (int i = 0 ; i < 10000; i ++) {
        dispatch_async(queue, ^{
           /*
            等待dispatch_smaphore
            一直等待,直到dispatch_semaphore的计数值达到大于或等于1
            */
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            /*
             由于dispatch_semaphore的计数值达到大于等于1
             所以将dispatch_semaphore的计数值减去1
             dispatch_semaphore_wait函数执行返回
             
             即此时的dispatch semaphore 恒为0
             
             由于可以访问NSMutableArray类对象的线程只有1个
             因此可以安全的进行更新
             */
            [array addObject:[NSNumber numberWithInt:i]];
            /*
             排他控制处理结束,所以通过dispatch_semaphore_signal函数将dispatch_samaphore的计数值加1.
             
             如果有通过dispatch_semaphore_wait函数等待dispatch_semaphore的计数值增加的线程,就由最先等待的线程执行。
             */
            dispatch_semaphore_signal(semaphore);
        });
    }

在没有serial dispatch queue和dispatch_barrier_async函数那么大粒度且一部分处理需要进行排他处理的情况下,diapatch_semaphore便可以发挥威力。

----Dispatch I/O  

在读取较大文件时,如果将文件分成合适的大小并使用global dispatch queue并列读取的话,会比一般的读取快不少。现在的输入/输出硬件已经可以做到一次使用多个线程更快的并列读取了。能实现这个功能的只有Dispatch I/O 和 Dispatch Data,这个后面更新。

 

posted @ 2017-12-13 16:24  梦影随风  阅读(187)  评论(0编辑  收藏  举报