并发编程-GCD 使用详解

GCD 

在项目中 偶尔 用到 GCD , 整理了一番,GCD 的 基本使用都囊括了。 

什么是 GCD ?

在这里 贴一下苹果《并发编程指南》中的介绍:

Grand Central Dispatch(GCD):系统管理线程,你不需要编写线程代码。只需定义想要执行的任务,然后添加到适当的 dispatchqueueGCD 会负责创建线程和调度你的任务。系统直接提供线程管理,比应用实现更加高效

优点:

l 直观而简单的编程接口

l 提供自动和整体的线程池管理

l 提供汇编级调优的速度

l 更加高效地使用内存

l 不会 trap 内核 underload

l 异步分派任务到 dispatch queue 不会导致 queue 死锁 l伸缩性强

l serial dispatch queue 比锁和其它同步原语更加高效

 

 总体上来说,GCD 技术 让我们 可以 轻而易举地 写 出 非常高效 的多线程的 代码,并比大多数情况下 手动 写的 管理 多线程的 代码还要高效。


  1. 什么是 GCD   一种 系统管理线程 的技术,不需要 编写 线程 代码,只需定义要执行的任务 block。然后添加到 适当的 dispatch queue 中。系统提供线程管理,比自行实现更加高效。
  2. 什么 是任务 block ? 要执行的 代码 块。
  3. 什么是 dispatch queue ?执行任务的机制,分为 serial concurrent 两种。注意队列都是先进先出的。
  4. 给个例子?

-(void)simpleGCD{

    NSLog(@"%s begin",__PRETTY_FUNCTION__);

    dispatch_queue_t myqueue = dispatch_queue_create("com.boyaa.texas.database",DISPATCH_QUEUE_CONCURRENT );//DISPATCH_QUEUE_SERIAL (or NULL)

    dispatch_async(myqueue, ^{

        [self heavyTask1];

    });

    NSLog(@"%s end",__PRETTY_FUNCTION__);

}

 

-(void)heavyTask1{

    NSLog(@"%s begin",__PRETTY_FUNCTION__);

    [NSThread sleepForTimeInterval:1];

    NSLog(@"%s end",__PRETTY_FUNCTION__);

}

 

simpleGCD 中,使用 dispatch_queue_create 创建 一个 myQueue , 然后 使用 dispatch_async  一个 block 放到 这个 myQueue 执行

执行 顺序 如下:

ConcurrentProgramingExample[2437:60b]-[XxpViewController simpleGCD] begin

2014-06-30 00:26:37.351ConcurrentProgramingExample[2437:60b] -[XxpViewController simpleGCD] end

2014-06-30 00:26:37.351ConcurrentProgramingExample[2437:1303] -[XxpViewController heavyTask1] begin

2014-06-30 00:26:38.353ConcurrentProgramingExample[2437:1303] -[XxpViewController heavyTask1] end

 

开始

   1.  serial  queue  串行队列,在队列里面的任务 按照 FIFO 的顺序,执行完一个任务才开始 下一个 任务

-(void)serialGCD1{

    NSLog(@"%s begin",__PRETTY_FUNCTION__);

    dispatch_queue_t myqueue = dispatch_queue_create("com.boyaa.texas.database",DISPATCH_QUEUE_SERIAL );//DISPATCH_QUEUE_SERIAL (or NULL)

    dispatch_async(myqueue, ^{

        [self heavyTask3];

    });

    dispatch_async(myqueue, ^{

        [self heavyTask1];

    });

    dispatch_async(myqueue, ^{

        [self heavyTask2];

    });

    NSLog(@"%s end",__PRETTY_FUNCTION__);

}

定义了 三个 任务 ,休眠时间如名称后缀。按顺序加入到队列中。观察 开始 和结束时间

2014-06-30 00:37:54.231ConcurrentProgramingExample[2571:60b] -[XxpViewController serialGCD1] begin

2014-06-30 00:37:54.232ConcurrentProgramingExample[2571:1303] -[XxpViewController heavyTask3] begin

2014-06-30 00:37:54.232ConcurrentProgramingExample[2571:60b] -[XxpViewController serialGCD1] end

2014-06-30 00:37:57.233ConcurrentProgramingExample[2571:1303] -[XxpViewController heavyTask3] end

2014-06-30 00:37:57.234ConcurrentProgramingExample[2571:1303] -[XxpViewController heavyTask1] begin

2014-06-30 00:37:58.236ConcurrentProgramingExample[2571:1303] -[XxpViewController heavyTask1] end

2014-06-30 00:37:58.236ConcurrentProgramingExample[2571:1303] -[XxpViewController heavyTask2] begin

2014-06-30 00:38:00.238ConcurrentProgramingExample[2571:1303] -[XxpViewController heavyTask2] end

 

  2. concurrent queue 并发队列,按FIFO 的顺序执行任务 。但执行完成时间 不定的。

-(void)concurrentGCD{

    NSLog(@"%s begin",__PRETTY_FUNCTION__);

    dispatch_queue_t myqueue = dispatch_queue_create("com.boyaa.texas.database",DISPATCH_QUEUE_CONCURRENT );

    dispatch_async(myqueue, ^{

        [self heavyTask3];

    });

    dispatch_async(myqueue, ^{

        [self heavyTask1];

    });

    dispatch_async(myqueue, ^{

        [self heavyTask2];

    });

    NSLog(@"%s end",__PRETTY_FUNCTION__);

}

创建 并发队列 (使用DISPATCH_QUEUE_CONCURRENT),然后使用 异步派发  任务加入到队列中。注意 任务的开始时间 和结束 时间。

2014-06-30 00:43:00.116ConcurrentProgramingExample[2620:60b] -[XxpViewController concurrentGCD] begin

2014-06-30 00:43:00.117ConcurrentProgramingExample[2620:60b] -[XxpViewController concurrentGCD] end

2014-06-30 00:43:00.117ConcurrentProgramingExample[2620:1303] -[XxpViewController heavyTask3] begin

2014-06-30 00:43:00.117ConcurrentProgramingExample[2620:3a03] -[XxpViewController heavyTask2] begin

2014-06-30 00:43:00.117ConcurrentProgramingExample[2620:3903] -[XxpViewController heavyTask1] begin

2014-06-30 00:43:01.129ConcurrentProgramingExample[2620:3903] -[XxpViewController heavyTask1] end

2014-06-30 00:43:02.129ConcurrentProgramingExample[2620:3a03] -[XxpViewController heavyTask2] end

2014-06-30 00:43:03.129ConcurrentProgramingExample[2620:1303] -[XxpViewController heavyTask3] end

 

  1. 系统提供了三个 并发 dispatch_queue  。有三个 优先级 

 

// 所有 高级别 线程启动 后,才会启动低级别 线程

#define DISPATCH_QUEUE_PRIORITY_HIGH 2

#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0

#define DISPATCH_QUEUE_PRIORITY_LOW (-2)

Eg :

 dispatch_queue_t myqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

 

当要 需要 并发 队列 ,应该 使用 系统提供的 全局 并发队列。不建议自行 创建。创建线程需要开销。

 

 

/*

@constant DISPATCH_QUEUE_PRIORITY_BACKGROUND

 * Items dispatched to the queue will run at backgroundpriority, i.e. the queue

 * will be scheduled for execution after all higherpriority queues have been

 * scheduled and the system will run items on this queue ona thread with

 * background status as per setpriority(2) (i.e. disk I/Ois throttled and the

 * thread's scheduling priority is set to lowest value).

 */

 

#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN

 

  1. 获取主线程

UI 更新代码  需要 在主线程 上运行。

dispatch_queue_tmyqueue = dispatch_get_main_queue();

 

  1. 添加任务到 Queue

 dispatch_async   : 异步地把任务加入到 队列中 ,并向下执行。

  dispatch_sync    : 同步添加任务 队列中,执行完任务才向下执行。

 

-(void)serialGCD2{

    NSLog(@"%s begin",__PRETTY_FUNCTION__);

    dispatch_queue_t myqueue = dispatch_queue_create("com.boyaa.texas.database",DISPATCH_QUEUE_CONCURRENT );    dispatch_sync(myqueue, ^{

        [self heavyTask3];

    });

    dispatch_sync(myqueue, ^{

        [self heavyTask1];

    });

    dispatch_sync(myqueue, ^{

        [self heavyTask2];

    });

    NSLog(@"%s end",__PRETTY_FUNCTION__);

}

注意 queue 并发的,但是并没有并发执行。其次注意 serialGCD2 bengin end 的位置,可与 serialGCD1 进行对比。

2014-06-30 01:11:37.100ConcurrentProgramingExample[2726:60b] -[XxpViewController serialGCD2] begin

2014-06-30 01:11:37.100ConcurrentProgramingExample[2726:60b] -[XxpViewController heavyTask3] begin

2014-06-30 01:11:40.102ConcurrentProgramingExample[2726:60b] -[XxpViewController heavyTask3] end

2014-06-30 01:11:40.103ConcurrentProgramingExample[2726:60b] -[XxpViewController heavyTask1] begin

2014-06-30 01:11:41.104ConcurrentProgramingExample[2726:60b] -[XxpViewController heavyTask1] end

2014-06-30 01:11:41.105ConcurrentProgramingExample[2726:60b] -[XxpViewController heavyTask2] begin

2014-06-30 01:11:43.107ConcurrentProgramingExample[2726:60b] -[XxpViewController heavyTask2] end

2014-06-30 01:11:43.107ConcurrentProgramingExample[2726:60b] -[XxpViewController serialGCD2] end

 

如何区分 dispatch_async dispatch_sync  , queue(DISPATCH_QUEUE_CONCURRENT) queue(DISPATCH_QUEUE_SERIAL)

前两个 添加 任务block queue 的方式。

后两个 queue 中执行 任务的 方式。

 

Tips :不管是执行 同步 /异步任务,都应该 使用 dispatch_async 进行添加 该任务到 queue 中。使用 dispatch_sync 会阻塞当前线程。

 

另外,绝对不要在任务   ,再次使用 dispatch_sync 同步新任务 当前queue 。对于串行queue,这样必定会造成死锁。对于 并发 queue ,也应该避免这样做。想想为什么:)  ?

 

  1.  dispatch group  可以 阻塞 一个 线程,直到  组内 任务 执行完成,才向下执行,并且组内任务完成后,会得到一个 任务完成时 通知。

-(void)dispatchGroupDemo{

    NSLog(@"%s begin",__PRETTY_FUNCTION__);

    dispatch_queue_t myqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_group_t mygroup = dispatch_group_create();

    

    dispatch_group_async(mygroup, myqueue, ^{

        [self heavyTask2];

    });

    dispatch_group_notify(mygroup, myqueue, ^{

        [self heavyTask1];

    });

    

    dispatch_group_wait(mygroup, DISPATCH_TIME_FOREVER);  //block thethread

    

    dispatch_group_async(mygroup, myqueue, ^{

        [self heavyTask3];

    });

    

    NSLog(@"%s end",__PRETTY_FUNCTION__);

}

使用 dispatch_group_create() 创建 一个 mygroup .Dispatch_group_async  添加 任务 block myqueue . 然后 dispatch_group_notify 添加 任务 完成 时的 通知block

使用 dispatch_goup_wait 阻塞 当前 线程(留意dispatchGroupDemo end 的时机,是在 heavy task2 end 之后才得以运行 ) 。后面又添加了一个 任务到 myqueue 中。

 

2014-06-30 21:56:23.171ConcurrentProgramingExample[5449:60b] -[XxpViewController dispatchGroupDemo]begin

2014-06-30 21:56:23.172ConcurrentProgramingExample[5449:1303] -[XxpViewController heavyTask2] begin

2014-06-30 21:56:25.174ConcurrentProgramingExample[5449:1303] -[XxpViewController heavyTask2] end

2014-06-30 21:56:25.175ConcurrentProgramingExample[5449:60b] -[XxpViewController dispatchGroupDemo]end

2014-06-30 21:56:25.175ConcurrentProgramingExample[5449:1303] -[XxpViewController heavyTask1] begin

2014-06-30 21:56:25.175ConcurrentProgramingExample[5449:3a03] -[XxpViewController heavyTask3] begin

2014-06-30 21:56:26.176ConcurrentProgramingExample[5449:1303] -[XxpViewController heavyTask1] end

2014-06-30 21:56:28.177ConcurrentProgramingExample[5449:3a03] -[XxpViewController heavyTask3] end

 

如果 dispatch_group_wait  屏蔽掉,是怎样的效果?

2014-06-30 21:58:37.244ConcurrentProgramingExample[5466:60b] -[XxpViewController dispatchGroupDemo]begin

2014-06-30 21:58:37.245ConcurrentProgramingExample[5466:60b] -[XxpViewController dispatchGroupDemo]end

2014-06-30 21:58:37.245ConcurrentProgramingExample[5466:1303] -[XxpViewController heavyTask2] begin

2014-06-30 21:58:37.245ConcurrentProgramingExample[5466:3407] -[XxpViewController heavyTask3] begin

2014-06-30 21:58:39.247ConcurrentProgramingExample[5466:1303] -[XxpViewController heavyTask2] end

2014-06-30 21:58:40.248ConcurrentProgramingExample[5466:3407] -[XxpViewController heavyTask3] end

2014-06-30 21:58:40.249ConcurrentProgramingExample[5466:3407] -[XxpViewController heavyTask1] begin

2014-06-30 21:58:41.250ConcurrentProgramingExample[5466:3407] -[XxpViewController heavyTask1] end

 

  1. Dispatch_barrier_async 隔断派发 。以 使用 派发方法 添加 任务block 到 queue 的时机为界线,在 之前的 任务 block  完成 后,才会开始执行此 任务 block . 在此 之后添加 任务 block ,需要等到 block 执行完成 后,才开始 执行。(注意  barrier 方式 添加任务 目标 queue ,必须是 自行创建的 并发queue )

-(void)dispatchbarrierDemo{

    NSLog(@"%s begin",__PRETTY_FUNCTION__);

    dispatch_queue_t myqueue =  dispatch_queue_create("com.boyaa.texas.barrier", DISPATCH_QUEUE_CONCURRENT);

    dispatch_async(myqueue, ^{

        [self heavyTask3];

    });

    

    dispatch_barrier_async(myqueue, ^{

        [self heavyTask1];

    });

    dispatch_async(myqueue, ^{

        [self heavyTask2];

    });

    NSLog(@"%s end",__PRETTY_FUNCTION__);

}

异步派发 task3 ,然后 隔断派发 task,最后 异步派发 task2 .执行 效果 ,task3 完成后,开始 task1 ,task1完成后,开始 task2.

2014-06-30 22:14:33.252ConcurrentProgramingExample[5579:60b] -[XxpViewController dispatchbarrierDemo]begin

2014-06-30 22:14:33.253ConcurrentProgramingExample[5579:60b] -[XxpViewController dispatchbarrierDemo]end

2014-06-30 22:14:33.253ConcurrentProgramingExample[5579:1303] -[XxpViewController heavyTask3] begin

2014-06-30 22:14:36.255ConcurrentProgramingExample[5579:1303] -[XxpViewController heavyTask3] end

2014-06-30 22:14:36.256ConcurrentProgramingExample[5579:1303] -[XxpViewController heavyTask1] begin

2014-06-30 22:14:37.257ConcurrentProgramingExample[5579:1303] -[XxpViewController heavyTask1] end

2014-06-30 22:14:37.258ConcurrentProgramingExample[5579:1303] -[XxpViewController heavyTask2] begin

2014-06-30 22:14:39.260ConcurrentProgramingExample[5579:1303] -[XxpViewController heavyTask2] end

 

Tips:

 

  1. 执行完成通知更新主线程

执行 任务的 block 里面 ,再次 dispatch  需要在主线程运行的任务   dispatch_get_main_queue()

 

-(void)doHeavyTaskAndThenUpdateUI{

    NSLog(@"%s begin",__PRETTY_FUNCTION__);

    dispatch_queue_t myqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_async(myqueue, ^{

        [self heavyTask3];

        NSString *str = @" get data here";

        

        dispatch_async(dispatch_get_main_queue(),^{

            //here update UI

            UILabel * label = [[UILabel alloc] initWithFrame: CGRectMake(30, 100, 300, 30)];

            label.text = str;

            [self.view addSubview:label];

            

        });

    });

    NSLog(@"%s end",__PRETTY_FUNCTION__);

}

queue 中,获取数据str 后,在 当前的 view 中创建一个 label ,并把内容显示出来

 

  1. 并发迭代。如果每次 迭代直接 没有 影响。可使用  dispatch_apply 并发地执行。

-(void)dispatchApply{

    int count = 10;

    dispatch_apply(count, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t i) {

        NSLog(@"current %zu",i);

    });

    

}

注意执行顺序是无序的

2014-06-30 01:35:17.496ConcurrentProgramingExample[2845:60b] current 1

2014-06-30 01:35:17.496ConcurrentProgramingExample[2845:3a03] current 2

2014-06-30 01:35:17.496ConcurrentProgramingExample[2845:3b03] current 3

2014-06-30 01:35:17.497ConcurrentProgramingExample[2845:60b] current 4

2014-06-30 01:35:17.496ConcurrentProgramingExample[2845:1303] current 0

2014-06-30 01:35:17.498ConcurrentProgramingExample[2845:3a03] current 5

2014-06-30 01:35:17.500ConcurrentProgramingExample[2845:60b] current 7

2014-06-30 01:35:17.500ConcurrentProgramingExample[2845:3b03] current 6

2014-06-30 01:35:17.501ConcurrentProgramingExample[2845:1303] current 8

2014-06-30 01:35:17.501ConcurrentProgramingExample[2845:3a03] current 9

 

  1. 后缀 _f  没有 _f  dispatch 系列函数是相同的 功能,不同 的是 使用 函数指针 而不是 block/对象 提交任务

void heavyTask4_f(){

    NSLog(@"%s begin",__PRETTY_FUNCTION__);

    [NSThread sleepForTimeInterval:4];

    NSLog(@"%s end",__PRETTY_FUNCTION__);

}

-(void)dispatchbarrier_fDemo{

    NSLog(@"%s begin",__PRETTY_FUNCTION__);

    dispatch_queue_t myqueue =  dispatch_queue_create("com.boyaa.texas.barrier", DISPATCH_QUEUE_CONCURRENT);

    dispatch_async(myqueue, ^{

        [self heavyTask3];

    });

    

    dispatch_barrier_async_f(myqueue, nil, &heavyTask4_f);

    dispatch_async(myqueue, ^{

        [self heavyTask2];

    });

    NSLog(@"%s end",__PRETTY_FUNCTION__);

}

 

  1. 消耗大内存的任务 应自行创建局部 autotreleasepool ,提交释放内存

 

  1. 挂起 继续 执行 queue  ,以及 挂起计数。使用 挂起 suspend可以 暂停 挂起queue 添加的任务block。此时 挂起 计数 +1 。使用 恢复 resume  ,挂起 计数 -1 .当挂起计数 0 时,可以 继续运行 前面被挂起的 任务 block

dispatch_suspend 挂起 计数 +1

 

挂起queue ,运行完当前的 queue 的所有任务block 后,暂停运行后面添加的block .

dispatch_resume 挂起计数 -1

 

挂起计数 0 时,运行完当前 queue 的所有任务 block 后,运行suspend 之后添加到 queue 的任务。

-(void)dispatch_suspend_resume{

    NSLog(@"%s begin",__PRETTY_FUNCTION__);

    myxxpQueue = dispatch_queue_create("com.boyaa.texas.resume", DISPATCH_QUEUE_CONCURRENT);

 

    dispatch_async(myxxpQueue, ^{

        [self heavyTask3];

    });

    dispatch_async(myxxpQueue, ^{

        [self heavyTask1];

        dispatch_resume(myxxpQueue);

    });

    dispatch_suspend(myxxpQueue);

 

    dispatch_async(myxxpQueue, ^{

        [self heavyTask2];

    });

    NSLog(@"%s end",__PRETTY_FUNCTION__);

    

}

2014-06-30 23:04:11.546ConcurrentProgramingExample[6059:60b] -[XxpViewControllerdispatch_suspend_resume] begin

2014-06-30 23:04:11.547ConcurrentProgramingExample[6059:60b] -[XxpViewControllerdispatch_suspend_resume] end

2014-06-30 23:04:11.547ConcurrentProgramingExample[6059:1303] -[XxpViewController heavyTask3] begin

2014-06-30 23:04:11.547ConcurrentProgramingExample[6059:3903] -[XxpViewController heavyTask1] begin

2014-06-30 23:04:12.549ConcurrentProgramingExample[6059:3903] -[XxpViewController heavyTask1] end

2014-06-30 23:04:14.549ConcurrentProgramingExample[6059:1303] -[XxpViewController heavyTask3] end

2014-06-30 23:04:14.550ConcurrentProgramingExample[6059:1303] -[XxpViewController heavyTask2] begin

2014-06-30 23:04:16.551ConcurrentProgramingExample[6059:1303] -[XxpViewController heavyTask2] end



-----------

参考资料:http://blog.csdn.net/totogo2010/article/details/8016129   

iOS 并发编程指南 》 ,kevin 翻译,http://www.gungyi.com 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值