GCD 多线程(一)

来源:互联网 发布:无线wifi软件 编辑:程序博客网 时间:2024/05/24 01:43


利用GCD下载大量图片的Demo

http://blog.csdn.net/tangbinqi_hua/article/details/6579843

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 将共享数据的访问代码封装起来。

[cpp] view plaincopyprint?
  1. - (id)something  
  2. {  
  3.     __block id localSomething;  
  4.     <span style="color:#ff0000;">dispatch_sync</span>(queue, ^{  
  5.         localSomething = [something retain];  
  6.     });  
  7.     return [localSomething autorelease];  
  8. }  
  9.   
  10. - (void)setSomething:(id)newSomething  
  11. {  
  12.     <span style="color:#ff0000;">dispatch_async</span>(queue, ^{  
  13.         if(newSomething != something)  
  14.         {  
  15.             [something release];  
  16.             something = [newSomething retain];  
  17.             [self updateSomethingCaches];  
  18.         }  
  19.     });  
  20. }  



dispatch_async 与 dispatch_get_global_queue的区别和联系:

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

dispatch queue是一个对象,它可以接受任务,并将任务以先到先执行的顺序来执行。dispatch 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会在后台异步执行。
[cpp] view plaincopyprint?
  1. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{  
  2.        [self goDoSomethingLongAndInvolved];  
  3.        NSLog(@"Done doing something long and involved");  
  4. );  


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

[cpp] view plaincopyprint?
  1. <pre name="code" class="cpp">- (id)something   
  2. { __block id localSomething;  
  3.  dispatch_sync(queue, ^{ localSomething = [something retain];   
  4.             });   
  5.     return [localSomething autorelease];   
  6. }  
  7.  - (void)setSomething:(id)newSomething   
  8. { dispatch_async(queue, ^{  
  9.      if(newSomething != something)   
  10.          {  
  11.                    [something release];   
  12.           something = [newSomething retain];   
  13.         [self updateSomethingCaches];   
  14.         }   });   
  15. }</pre><br><br><br>  

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

 
[cpp] view plaincopyprint?
  1. //dispatch_get_global_queue()是全局队列,并发执行  
  2.  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),  ^{  
  3.      for (int i=0; i<100; i++) {  
  4.          float newProgress = [progressView progress] + 0.01;  
  5.          //刷新主线程  
  6.          dispatch_async(dispatch_get_main_queue(), ^{  
  7.              progressView.progress = newProgress;  
  8.          });  
  9.          usleep(1000*100);  
  10.      } //for loop  
  11.  });//dispatch_async  

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

[cpp] view plaincopyprint?
  1. for(id obj in array)  
  2.     [self doSomethingIntensiveWith:obj];  
  3.  [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,同时将它们添加至一个组。所以我们现在可以重新编码:

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

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


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

[cpp] view plaincopyprint?
  1. dispatch_queue_t queue = dispatch_get_global_qeueue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);  
  2.     dispatch_apply([array count], queue, ^(size_t index){  
  3.         [self doSomethingIntensiveWith:[array objectAtIndex:index]];  
  4.     });  
  5.     [self doSomethingWith:array];  

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

[cpp] view plaincopyprint?
  1. dispatch_queue_t queue = dispatch_get_global_qeueue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);  
  2. dispatch_async(queue, ^{  
  3.     dispatch_apply([array count], queue, ^(size_t index){  
  4.         [self doSomethingIntensiveWith:[array objectAtIndex:index]];  
  5.     });  
  6.     [self doSomethingWith:array];  
  7. });  

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中的所有程序块即将运行完成时 再执行。
原创粉丝点击