iOS开发-多线程之GCD(Grand Central Dispatch)

十度 IOS 2015年12月01日 收藏

Grand Central Dispatch(GCD)是一个强有力的方式取执行多线程任务,不管你在回调的时候是异步或者同步的,可以优化应用程序支持多核心处理器和其他的对称多处理系统的系统。开发使用的过程中只需要将执行的任务并添加到到适当的Dispatch Queue中,GCD就能生成必要的线程并计划执行任务。Dispatch Queue更简单而且在实现符合需求的多线程任务时更有效率。Dispatch  Queue一般来说有三种方式,如下图:

 Serial执行的时候的先进先出,Concurrent是并发执行任务,main属于全局可用的quene;从定义上看我们可以知道Serial放在一个队列里面,占用一个线程,ConCurrent则占用多个线程,具体数量由系统决定,执行的顺序是随机的。

Dispatch Quene简单调用:

    //调用队列的两种不同的方式
   dispatch_queue_t  quene = dispatch_queue_create("http://www.cnblogs.com/xiaofeixiang", NULL);
    //异步调用执行
    dispatch_async(quene, ^{
        NSLog(@"dispatch_async简单调用-FlyElephant");
    });
    //同步
    dispatch_sync(quene, ^{
        NSLog(@"同步执行-FlyElephant");
    });

 获取全局的队列和主队列的方式,一般更新UI的时候会使用到Main Dispatch Queue,全局的队列比较实际操作中比较常用:

    //获取Main Queue
        dispatch_queue_t mainQueue =dispatch_get_main_queue();
    //获取全局的 Global Queue
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_sync(globalQueue, ^{
        NSLog(@"dispatch_get_global_queue调用-FlyElephant");
    });

 Main使用的时候需要注意的使用异步方式,不然页面会卡死,Main和Global是全局对象,不需要释放,系统会自动处理;

    dispatch_queue_t mainQueue =dispatch_get_main_queue();
    
    dispatch_async(mainQueue, ^{
        NSLog(@"dispatch_get_main_queue调用-FlyElephant");
        self.myImageView.image=[UIImage imageNamed:[NSString stringWithFormat:@"Thread2.jpg"]];
    });

延时执行任务dispatch_after,dispatch_after的确切的表示应该是在一定的时间后把任务添加进队列中,如果对时间精度有要求,需要自己根据需求改改:

    dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW,10);
    dispatch_after(time, globalQueue,^{
        NSLog(@"延迟执行");
    });
dispatch_apply其用法就像for的用法,可以指定的block执行指定的次数。如果要对某个数组中的所有元素执行同样的block的时候,这个函数就显得很有用了,用法很简单,指定执行的次数以及Dispatch Queue,在block回调中会带一个索引,然后就可以根据这个索引来判断当前是对哪个元素进行操作,
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_apply(36, globalQueue, ^(size_t i) {
            NSLog(@"%zu",i);
        });

 控制看结果会发现结果是:

2015-02-11 22:54:29.602 ThreadDemo[1404:54736] 2
2015-02-11 22:54:29.602 ThreadDemo[1404:54734] 3
2015-02-11 22:54:29.602 ThreadDemo[1404:54707] 0
2015-02-11 22:54:29.603 ThreadDemo[1404:54736] 4
2015-02-11 22:54:29.603 ThreadDemo[1404:54734] 5
2015-02-11 22:54:29.603 ThreadDemo[1404:54707] 6
2015-02-11 22:54:29.603 ThreadDemo[1404:54736] 7
2015-02-11 22:54:29.603 ThreadDemo[1404:54707] 9
2015-02-11 22:54:29.603 ThreadDemo[1404:54734] 8
2015-02-11 22:54:29.602 ThreadDemo[1404:54733] 1
2015-02-11 22:54:29.603 ThreadDemo[1404:54707]  dispatch_apply执行后

Global是并发队列(Concurrent Dispatch Queue),不能保证执行的先后顺序,但dispatch_apply函数是同步的,执行过程中会使线程在此处停住,我们可以在一个异步线程里使用dispatch_apply函数,稍微改改:

    dispatch_async(globalQueue, ^{
        dispatch_apply(10, globalQueue, ^(size_t i) {
            NSLog(@"%zu",i);
        });
    });
    NSLog(@"dispatch_apply执行后");

修改之后的执行结果:

2015-02-11 22:58:24.321 ThreadDemo[1433:56214] dispatch_apply执行后
2015-02-11 22:58:24.321 ThreadDemo[1433:56285] 3
2015-02-11 22:58:24.321 ThreadDemo[1433:56282] 2
2015-02-11 22:58:24.323 ThreadDemo[1433:56285] 4
2015-02-11 22:58:24.321 ThreadDemo[1433:56284] 0
2015-02-11 22:58:24.323 ThreadDemo[1433:56282] 5
2015-02-11 22:58:24.321 ThreadDemo[1433:56283] 1
2015-02-11 22:58:24.323 ThreadDemo[1433:56285] 6
2015-02-11 22:58:24.323 ThreadDemo[1433:56284] 7
2015-02-11 22:58:24.323 ThreadDemo[1433:56282] 8
2015-02-11 22:58:24.323 ThreadDemo[1433:56283] 9

 Dispatch Queue暂停与恢复,这个基本没什么说的,就一方法的调用:

    //暂停
    dispatch_suspend(globalQueue);
    //恢复
    dispatch_resume(globalQueue);

dispatch_barrier_async:前面的任务执行结束后才执行,而且之后的任务等它执行完成之后才能执行,具体参考代码:

    dispatch_queue_t queue = dispatch_queue_create("http://www.cnblogs.com/xiaofeixiang", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:1];
        NSLog(@"dispatch_async-keso");
    });
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"dispatch_async-FlyElephant");
    });
    dispatch_barrier_async(queue, ^{
         [NSThread sleepForTimeInterval:3];
        NSLog(@"dispatch_barrier_async-博客园");
    });
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:4];
        NSLog(@"dispatch_async-敏捷大拇指");
    });

 最终执行结果如下,注意观察时间:

2015-02-11 23:08:40.668 ThreadDemo[1475:59699] dispatch_async-keso
2015-02-11 23:08:41.664 ThreadDemo[1475:59698] dispatch_async-FlyElephant
2015-02-11 23:08:44.670 ThreadDemo[1475:59698] dispatch_barrier_async-博客园
2015-02-11 23:08:48.675 ThreadDemo[1475:59698] dispatch_async-敏捷大拇指

 dispatch_group:Queue中dispatch_group_wait会等待之前的任务,如果之前的任务比较耗时的话,那么线程阻塞,使用需要谨慎,另外wait的时候可以指定时间的,例子中DISPATCH_TIME_FOREVER表示永远等待,如果是在OS X 10.8或iOS 6以及之后版本中使用,Dispatch Group将会由ARC自动管理,如果是在此之前的版本,需要自己手动释放。

    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(@"Group任务One");
    });
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"Group任务Two");
    });
    

    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    
    NSLog(@"Group任务已经完成");

  dispatch_semaphore可以简单理解成信号量,先创建一个信号量,然后等待回调执行任务,执行任务之后要释放,主要的处理点在wait等待上面,简单的代码如下:

    dispatch_semaphore_t fd_sema = dispatch_semaphore_create(getdtablesize() / 2);
    //等待
    dispatch_semaphore_wait(fd_sema, DISPATCH_TIME_FOREVER);
  
    NSLog(@"dispatch_semaphore测试");
    //释放
    dispatch_semaphore_signal(fd_sema);

  被要求睡觉,如果有写的不当的地方,多多指正~