GCD 多线程(一)


利用GCD下载大量图片的Demo


GCD简介:

http://www.dreamingwish.com/dream-2012/gcd介绍(一)-基本概念和dispatch-queue.html

http://blog.csdn.net/liuhongwei123888/article/details/6899366

GCD不需要使用锁,但是可以用 用户队列(必须用用户队列[dispatch_queue_create ],而非全局队列) 替代锁来完成同步机制。然后可以用dispatch_async  或者 dispatch_sync 将共享数据的访问代码封装起来。

    - (id)something
    {
        __block id localSomething;
        dispatch_sync(queue, ^{
            localSomething = [something retain];
        });
        return [localSomething autorelease];
    }

    - (void)setSomething:(id)newSomething
    {
        dispatch_async(queue, ^{
            if(newSomething != something)
            {
                [something release];
                something = [newSomething retain];
                [self updateSomethingCaches];
            }
        });
    }



dispatch_async 与 dispatch_get_global_queue的区别和联系:

dispatch_sync:提交块对象给指定的调剂队列,同步履行。
dispatch_async:提交块对象给指定的调剂队列,异步履行。
dispatch_async() 调用以后立即返回。
dispatch_sync() 调用以后等到block执行完以后才返回 。dispatch_sync()会阻塞当前线程。
[NSThread isMainThread]  是判断当前线程是否为主线程,

dispatch queue是一个对象,它可以接受任务,并将任务以先到先执行的顺序来执行。d ispatch queue可以是并发的或串行的。

GCD中有三种队列类型:

  1. The main queue: 与主线程功能相同。实际上,提交至main queue的任务会在主线程中执行。main queue可以调用dispatch_get_main_queue()来获得。因为main queue是与主线程相关的,所以这是一个串行队列。
  2. Global queues: 全局队列是并发队列,并由整个进程共享。进程中存在三个全局队列:高、中(默认)、低三个优先级队列。可以调用dispatch_get_global_queue函数传入优先级来访问队列。
  3. 用户队列: 用户队列 (GCD并不这样称呼这种队列, 但是没有一个特定的名字来形容这种队列,所以我们称其为用户队列) 是用函数 dispatch_queue_create 创建的队列. 这些队列是串行的。正因为如此,它们可以用来完成同步机制, 有点像传统线程中的mutex。


创建队列:
要使用用户队列,我们首先得创建一个。调用函数dispatch_queue_create就行了。函数的第一个参数是一个标签,这纯是为了debug。第二个参数目前不支持,传入NULL就行。
提交Job:
向一个队列提交Job很简单:调用dispatch_async函数,传入一个队列和一个block。队列会在轮到这个block执行时执行这个block的代码。下面的例子是一个在后台执行一个巨长的任务://该方法不用等到block语法执行完,而是立马就返回,block会在后台异步执行。
 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [self goDoSomethingLongAndInvolved];
        NSLog(@"Done doing something long and involved");
});


在典型的Cocoa程序中,你很有可能希望在任务完成时更新界面,这就意味着需要在主线程中执行一些代码。你可以简单地完成这个任务——使用嵌套的dispatch,在外层中执行后台任务,在内层中将任务dispatch到main queue:代码如下:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [self goDoSomethingLongAndInvolved];
        dispatch_async(dispatch_get_main_queue(), ^{
            [textField setStringValue:@"Done doing something long and involved"];
        });
});
要用于同步机制,queue必须是一个用户队列,而非全局队列,所以使用using dispatch_queue_create 初始化一个。然后可以用 dispatch_async   或者  dispatch_sync 将共享数据的访问代码封装起来:

- (id)something 
{ __block id localSomething;
 dispatch_sync(queue, ^{ localSomething = [something retain]; 
			}); 
	return [localSomething autorelease]; 
}
 - (void)setSomething:(id)newSomething 
{ dispatch_async(queue, ^{
	 if(newSomething != something) 
		 {
				   [something release]; 
		  something = [newSomething retain]; 
		[self updateSomethingCaches]; 
		}   }); 
}



 值得注意的是dispatch queue是非常轻量级的,就像你以前使用lock一样。







 
   //dispatch_get_global_queue()是全局队列,并发执行
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),  ^{
        for (int i=0; i<100; i++) {
            float newProgress = [progressView progress] + 0.01;
            //刷新主线程
            dispatch_async(dispatch_get_main_queue(), ^{
                progressView.progress = newProgress;
            });
            usleep(1000*100);
        } //for loop
    });//dispatch_async

有时候我们有一段代码要像这样操作一个数组,但是在操作完成后,我们还需要对操作结果进行其他操作:

for(id obj in array)
    [self doSomethingIntensiveWith:obj];
 [self doSomethingWith:array];

这时候使用GCD的dispatch_async 就悲剧了,我们还不能简单的使用dispatch_sync来解决这个问题,因为这将导致每个迭代器阻塞,就完全破坏了平行计算。


A:用三个线程串行执行做一件事,完成之后回调finish方法。

B:把三个线程放在group里面,完成之后回调finish方法。

A和B的区别是:A三个线程是串行的而B三个线程是并行的,都是在线程完成之后回调finish的方法。


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

dispatch_queue_t queue = dispatch_get_global_qeueue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
for(id obj in array)
    dispatch_group_async(group, queue, ^{
        [self doSomethingIntensiveWith:obj];
    });
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_release(group);
 
[self doSomethingWith:array];
如果这些工作可以异步执行,那么我们可以更风骚一点,将函数 -doSomethingWith:放在后台执行。我们使用dispatch_group_async函数建立一个block在组完成后执行:

dispatch_queue_t queue = dispatch_get_global_qeueue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
for(id obj in array)
    dispatch_group_async(group, queue, ^{
        [self doSomethingIntensiveWith:obj];
    });
dispatch_group_notify(group, queue, ^{
    [self doSomethingWith:array];
});
dispatch_release(group);
不仅所有数组元素都会被平行操作,后续的操作也会异步执行,并且这些异步运算都会将程序的其他部分的负载考虑在内。注意如果 -doSomethingWith:需要在主线程中执行,比如操作GUI,那么我们只要将main queue而非全局队列传给dispatch_group_notify函数就行了。


对于同步执行,GCD提供了一个简化方法叫做dispatch_apply。这个函数调用单一block多次,并平行运算,然后等待所有运算结束,就像我们想要的那样

dispatch_queue_t queue = dispatch_get_global_qeueue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_apply([array count], queue, ^(size_t index){
        [self doSomethingIntensiveWith:[array objectAtIndex:index]];
    });
    [self doSomethingWith:array];

这很棒,但是异步咋办?dispatch_apply函数可是没有异步版本的。但是我们使用的可是一个为异步而生的API啊!所以我们只要用dispatch_async函数将所有代码推到后台就行了:

dispatch_queue_t queue = dispatch_get_global_qeueue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
    dispatch_apply([array count], queue, ^(size_t index){
        [self doSomethingIntensiveWith:[array objectAtIndex:index]];
    });
    [self doSomethingWith:array];
});

dispatch_group_wait(group,timeout) 与 dispatch_group_notify 的区别:
之后我试了以下 dispatch_group_wait( .. , .. ) 这个函数
把dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{ });
替换成:  dispatch_group_wait (group, DISPATCH_TIME_FOREVER );
再在最后加上   dispatch_release (group);     
//因为官方文档中说group 不再用时release,但是在creat函数中没有特殊说明需要release , 加上release之后测试了几次也没报错, 想下就先加上了。

实现的功能和之前一样,再说下 dispatch_group_wait (group,  timeout ) 这个函数,
第一个参数是:需要等待的group。
第二个参数是:设定的等待时间。
效果: 等待group是否为空group,如果在timeout时间内group为空了,则返回0;否则返回非0。 如果timeout为 DISPATCH_TIME_FOREVER,那就是一直阻塞着,直到group内内容完全处理完在执行。

书中例子用到的 dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{});
则是表示 指定一个额外的程序块,它将在group中的所有程序块即将运行完成时 再执行。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值