IOS多线程之——GCD(二)

多核心的性能。

概念:

为了在单一进程中充分发挥多核的优势,我们有必要使用多线程技术(我们没必要去提多进程,这玩意儿和GCD没关系)。在低层,GCD全局dispatch queue仅仅是工作线程池的抽象。这些队列中的Block一旦可用,就会被dispatch到工作线程中。提交至用户队列的Block最终也会通过全局 队列进入相同的工作线程池(除非你的用户队列的目标是主线程,但是为了提高运行速度,我们绝不会这么干)。

有两种途径来通过GCD“榨取”多核心系统的性能:
将单一任务或者一组相关任务并发至全局队列中运算;
将多个不相关的任务或者关联不紧密的任务并发至用户队列中运算;

全局队列。
设想下面的循环:
    [_array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        [self doSomethingIntensiveWith:obj];
    }];

假定 -doSomethingIntensiveWith: 是线程安全的且可以同时执行多个.一个array通常包含多个元素,这样的话,我们可以很简单地使用GCD来平行运算:
dispatch_queue_t queue_global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
    [_array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        dispatch_async(queue_global, ^{
            [self doSomethingIntensiveWith:obj];
        });
    }];
如此简单,我们已经在多核心上运行这段代码了。

当然这段代码并不完美。有时候我们有一段代码要像这样操作一个数组,但是在操作完成后,我们还需要对操作结果进行其他操作:
    [_array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        [self doSomethingIntensiveWith:obj];
    }];
    [self doSomethingWith:_array];
这时候使用GCD的 dispatch_async 就悲剧了.我们还不能简单地使用dispatch_sync来解决这个问题因为这将导致每个迭代器阻塞,就完全破坏了平行计算。

解决这个问题的一种方法是使用dispatch group。一个dispatch group可以用来将多个block组成一组以监测这些Block全部完成或者等待全部完成时发出的消息。使用函数 dispatch_group_create来创建,然后使用函数dispatch_group_async来将block提交至一个dispatch queue,同时将它们添加至一个组。所以我们现在可以重新编码:

    dispatch_queue_t queue_global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
    dispatch_group_t group = dispatch_group_create();
    [_array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        dispatch_group_async(group,queue_global, ^{
            [self doSomethingIntensiveWith:obj];
        });
    }];
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    [self doSomethingWith:_array];

如果这些工作可以异步执行,那么我们可以更风骚一点,将函数-doSomethingWith:放在后台执行。我们使用dispatch_group_async函数建立一个block在组完成后执行:
    dispatch_queue_t queue_global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
    dispatch_group_t group = dispatch_group_create();
    [_array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        dispatch_group_async(group,queue_global, ^{
            [self doSomethingIntensiveWith:obj];
        });
    }];
    dispatch_group_notify(group, queue_global, ^{
        [self doSomethingWith:_array];
    });

不仅所有数组元素都会被平行操作,后续的操作也会异步执行,并且这些异步运算都会将程序的其他部分的负载考虑在内。 如果doSomethingWith:需要在主线程中执行,比如操作GUI,那么我们只要将main queue而非全局队列传给dispatch_group_notify函数就行了。


对于同步执行,GCD提供了一个简化方法叫做dispatch_apply。这个函数调用单一block多次,并平行运算,然后等待所有运算结束,就像我们想要的那样:
    dispatch_queue_t queue_global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
    dispatch_apply([_array count],queue_global, ^(size_t index){
        [self doSomethingIntensiveWith:[_array objectAtIndex:index]];
    });
    [self doSomethingWith:_array];
这很棒,但是异步咋办?dispatch_apply函数可是没有异步版本的。但是我们使用的可是一个为异步而生的API啊!所以我们只要用dispatch_async函数将所有代码推到后台就行了:
dispatch_queue_t queue_global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
    dispatch_async(queue_global, ^{
        dispatch_apply([_array count],queue_global, ^(size_t index){
            [self doSomethingIntensiveWith:[_array objectAtIndex:index]];
        });
        [self doSomethingWith:_array];
    });

这种方法的关键在于确定我们的代码是在一次对不同的数据片段进行相似的操作。如果你确定你的任务是线程安全的(不在本篇讨论范围内)那么你可以使用GCD来重写你的循环了,更平行更风骚。

要看到性能提升,你还得进行一大堆工作。比之线程,GCD是轻量和低负载的,但是将block提交至queue还是很消耗资源的——block需要被拷贝 和入队,同时适当的工作线程需要被通知。不要将一张图片的每个像素作为一个block提交至队列,GCD的优点就半途夭折了。如果你不确定,那么请进行试 验。将程序平行计算化是一种优化措施,在修改代码之前你必须再三思索,确定修改是有益的(还有确保你修改了正确的地方)。




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值