关于线程问题的一些总结

来源:互联网 发布:今年淘宝双11销售额 编辑:程序博客网 时间:2024/06/04 17:53

转自: http://blog.csdn.net/lxl_815520/article/details/50736706



1.创建线程的几种方式(MRC) 
1>创建线程的第一种方式,NSThread类方法
方法一
[objc] view plain copy
在CODE上查看代码片派生到我的代码片
  1. NSThread *thread= [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"universe"];  
  2. [threadstart];  
  3. [threadrelease];  
方法二
[objc] view plain copy
在CODE上查看代码片派生到我的代码片
  1. [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"yuzhou"];  
2>创建线程的第二种方法  NSObject方法
[objc] view plain copy
在CODE上查看代码片派生到我的代码片
  1. >[self performSelectorInBackground:@selector(run:) withObject:@"nsobject thread"];  
3>创建线程的第三种方式
[objc] view plain copy
在CODE上查看代码片派生到我的代码片
  1. 方法一  
  2.  NSOperationQueue *oprationQueue = [[NSOperationQueue alloc] init];  
  3.  [oprationQueue addOperationWithBlock:^{  
  4.         //这个block语句块在子线程中执行  
  5.         NSLog(@"oprationQueue");  
  6.     }];  
  7.  [oprationQueue release];  
  8.       
  9. 方法二  
  10.  NSOperationQueue *oprationQueue1 = [[NSOperationQueue alloc] init];  
  11.  oprationQueue1.maxConcurrentOperationCount = 1;//指定池子的并发数  
  12.  //NSOperation 相当于java中的runnable接口,继承自它的就相当一个任务  
  13.  NSInvocationOperation *invation = [[NSInvocationOperation alloc] initWithTarget:self selector:  
  14. @selector(run:) object:@"invation"];  
  15.  [oprationQueue1 addOperation:invation];//将任务添加到池子里面,可以给池子添加多个任务,并且指定它的并发数  
  16. [invation release];  
  17.  //第二个任务  
  18.  NSInvocationOperation *invation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:  
  19. @selector(run2:) object:@"invocation2"];  
  20. invation2.queuePriority = NSOperationQueuePriorityHigh;//设置线程优先级  
  21. [oprationQueue1 addOperation:invation2];  
  22. [invation2 release];  
  23.  [oprationQueue1 release];  
4>创建多线程第四种方式 GCD 支持多核,高效率的多线程技术
[objc] view plain copy
在CODE上查看代码片派生到我的代码片
  1.  dispatch_queue_t queue = dispatch_queue_create("name"NULL);  
  2.     //创建一个子线程  
  3.     dispatch_async(queue, ^{  
  4.         // 子线程code... ..  
  5.         NSLog(@"GCD多线程");  
  6.            
  7.         //回到主线程  
  8.         dispatch_sync(dispatch_get_main_queue(), ^{//其实这个也是在子线程中执行的,只是把它放到了主线程的队列中  
  9.             Boolean isMain = [NSThread isMainThread];  
  10.             if(isMain) {  
  11.                 NSLog(@"GCD主线程");  
  12.             }  
  13.         });  
  14.     });  
  15.   
  16. 注释:线程的执行方法   
  17. - (void)onMain  
  18. {  
  19.     Boolean b = [NSThread isMainThread];  
  20.     if(b) {  
  21.         NSLog(@"onMain;;%d",b);  
  22.     }  
  23. }  
  24. - (void) run:(NSString*)str  
  25. {  
  26.     NSLog(@"多线程运行:::%@",str);  
  27. }</span>  

2.Objective-C 中实现多线程

NSObject 提供了以 performSelector 为前缀的一系列方法。它们可以让用户在指定线程中,或者立即,或者延迟执行某个方法调用。
这个方法给了用户实现多线程编程最简单的方法。下面有一些例子:
1>NSObject方法
调用主线程
[objc] view plain copy
在CODE上查看代码片派生到我的代码片
  1. [self performSelectorOnMainThread:@selector(onMain) withObject:self waitUntilDone:YES];  
  2. [self performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait   
  3. modes:(NSArray *)array]  

注释:用来子线程和主线程交互,最后面的那个boolean参数,如果为yes就是等这个方法执行完了在执行后面的代码;
如果为no的话,就是不管这个方法执行完了没有,接着往下走
注意:
1.当前线程为主线程的时候,waitUntilDone:YES参数无效。
2.该方法,没有返回值
3.该方法主要用来用主线程来修改页面UI的状态。
创建子线程
[objc] view plain copy
在CODE上查看代码片派生到我的代码片
  1. [self performSelectorInBackground:@selector(run:) withObject:@"nsobject thread"];  
注释:创建一个线程在子线程执行,aSelector代表了新创建的线程,arg是传入的参数
[objc] view plain copy
在CODE上查看代码片派生到我的代码片
  1. 在当前线程中执行方法  
  2. [self performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay]  
  3. [performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay  
  4.  inModes:(NSArray *)modes]  
  5. 在指定线程中执行方法  
  6. [self performSelector:(SEL)aSelector onThread:(NSThread *)thread withObject:(id)arg   
  7. waitUntilDone:(BOOL)wait]  
  8. [self performSelector:(SEL)aSelector onThread:(NSThread *)thread withObject:(id)arg   
  9. waitUntilDone:(BOOL)wait modes:(NSArray *)array]  
这一系列方法简单易用,但只提供了有限的几个选择:指定执行的方法(但传入方法的参数数量有限制);
指定是在当前线程,还是在主线程,还是在后台线程执行;指定是否需要阻塞当前线程等待结果。
例如,以下代码使得方法 foo: 在一个新的后台线程执行,并传入了 object 参数:
SEL selector = @selector(foo:);
[self performSelectorInBackground:selector withObject:object];
以下代码使得 updateUI 方法在主线程内得到执行,并且当前线程会被阻塞,直到主线程执行完该函数:
[self performSelectorOnMainThread:@selector(updateUI) withObject:nil waitUntilD

2>NSThread类方法

NSThread 是 OS X 和 iOS 都提供的一个线程对象,它是线程的一个轻量级实现。在执行一些轻量级的简单任务时,
NSThread 很有用,但用户仍然需要自己管理线程生命周期,进行线程间同步。比如,线程状态,依赖性,线程间同步
等线程相关的主题 NSThread 都没有涉及。
比如,涉及到线程间同步仍然需要配合使用 NSLock,NSCondition 或者 @synchronized。
所以,遇到复杂任务时,轻量级的 NSThread 可能并不合适。
提供一个模拟多线程运作的简单例子:两个人同时一起到烤箱抢面包。我们启动两个线程,来代表两个人。
由于烤箱门比较小,同时只能有一个人去拿面包。
由于 NSThread 不处理线程同步,所以为了模拟这个过程, 你还需要一把线程锁(即类型为 NSLock 的实例变量 _lock)。
在后面的 run 方法中会用到这把线程锁
[objc] view plain copy
在CODE上查看代码片派生到我的代码片
  1. _lock = [[NSLock alloc] init];  
  2. NSThread *geroge = [[NSThread alloc] itWithTarget:self selector:@selector(run) object:nil];  
  3. [geroge setName:@"Geroge"];  
  4. [geroge start];  
  5.   
  6. NSThread *totty = [[NSThread alloc] nitWithTarget:self selector@selector(run)  object:nil];  
  7. [totty setName:@"Totty"];  
  8. [totty start];  
受到线程锁保护的拿面包过程可以用下面的 run 方法表示
[objc] view plain copy
在CODE上查看代码片派生到我的代码片
  1. - (void)run {  
  2. while (TRUE) {  
  3. [_lock lock];  
  4. if(_cake > 0){  
  5. [NSThread sleepForTimeInterval:0.5];  
  6. _cake--;  
  7. _occupied = kSum - _cake;   
  8. NSLog(@"Taken by %@\nCurrent free:%ld, occupied:%ld", [[NSThread currentThread] name], _cake, _occupied);  
  9. }  
  10. [_lock unlock];  
  11. }  
  12. }  
3>NSOperation
NSOperation做的事情比 NSThread 更多一些。通过继承 NSOperation,可以使子类获得一些线程相关的特性,
进而可以安全地管理线程生命周期。

比如,以线程安全的方式建立状态,取消线程。配合 NSOperationQueue,可以控制线程间的优先级和依赖性。
这就给出了一套线程管理的基本方法。NSOperation代表了一个独立的计算单元。一般,我们会把计算任务封
装进 NSOperation 这个对象。NSOperation 是抽象类,但同时也提供了两个可以直接使用的实体子类:NSInvocationOperation
 和 NSBlockOperation。NSInvocationOperation 用于将计算任务封装进方法,NSBlockOperation 
用于将计算任务封装进 block。

NSOperationQueue则用于执行计算任务,管理计算任务的优先级,处理计算任务之间的依赖性。
NSOperation 被添加到 NSOperationQueue 中之后,队列会按优先级和进入顺序调度任务,
NSOperation 对象会被自动执行。
仍然使用上一节 NSThread 中的模拟两人抢面包的例子。由于计算任务没有变化,所以 run 方法并不改变。
但这里需要使用 NSOperation 和 NSOperationQueue 来代表两个抢面包的人,并给予他们不同的优先级。
由于 NSOperation 也不处理线程间同步问题,所以你仍然需要一把在 run 方法中会用到的线程锁:
[objc] view plain copy
在CODE上查看代码片派生到我的代码片
  1. _lock = [[NSLock alloc] init];  
  2. NSInvocationOperation *geroge = [[NSInvocationOperation alloc]initWithTarget:self  selector:@selector(run:)    
  3. object:@"Geroge"];  
  4. geroge.queuePriority = NSOperationQueuePriorityHigh;  
  5. NSInvocationOperation *operationTwo = [[NSInvocationOperation alloc]initWithTarget:self  selector:@selector(run:)  
  6.  object:@"Totty"];  
  7. totty.queuePriority = NSOperationQueuePriorityLow;  
  8. NSOperationQueue *queue = [[NSOperationQueue alloc] init];  
  9. [queue setMaxConcurrentOperationCount:2];  
  10. [queue addOperation:geroge];  
  11. [queue addOperation:totty];  

NSOperation提供以下任务优先级,以这些优先级设置变量 queuePriority 即可加快或者推迟操作的执行:
NSOperationQueuePriorityVeryHigh
NSOperationQueuePriorityHigh
NSOperationQueuePriorityNormal
NSOperationQueuePriorityLow
NSOperationQueuePriorityVeryLow
NSOperation使用状态机模型来表示状态。通常,你可以使用 KVO(Key-Value Observing)观察任务的执行状态。
这是其他多线程工具所不具备的功能。NSOperation 提供以下状态:
isReady
isExecuting
isFinished
NSOperation对象之间的依赖性可以用如下代码表示:
[objc] view plain copy
在CODE上查看代码片派生到我的代码片
  1. [refreshUIOperation addDependency:requestDataOperation];  
  2. [operationQueue addOperation:requestDataOperation];  
  3. [operationQueue addOperation:refreshUIOperation];  
除非 requestDataOperation 的状态 isFinished 返回 YES,不然 refreshUIOperation 这个操作不会开始。

NSOperation还有一个非常有用功能,就是“取消”。这是其他多线程工具(包括后面要讲到的 GCD)都没有的。
调用 NSOperation 的 cancel: 方法即可取消该任务。当你知道这个任务没有必要再执行下去时,尽早安全地取消它将有利于节省系统资源。
4>GCD
GCD(Grand Central Dispatch)是 Apple 公司为了提高 OS X 和 iOS 系统在多核处理器上运行并行代码的能力
而开发的一系列相关技术,它提供了对线程的高级抽象。GCD 是一整套技术,包含了语言级别的新功能,运行时库,系统级别的优化,
这些一起为并发代码的执行提供了系统级别的广泛优化。所以,GCD 也是 Apple 推荐的多线程编程工具。

GCD是系统层面的技术,除了可以被系统级应用使用,也可以被更普通的高级应用使用。使用 GCD 之后,应用就可以轻松地在多核系统上
高效运行并发代码,而不用考虑繁琐的底层问题。GCD 在系统层面工作,能很好地满足所有应用的并行运行需求,将可用系统资源平衡地分配给它们。

GCD提供了一套纯 C API。但是,它提供的 API 简单易用并且有功能强大的任务管理和多线程编程能力。GCD 需要和 blocks(Objective-C 的闭包)配合使用
。block 是 GCD 执行单元。GCD 的任务需要被拆解到 block 中。block 被排入 GCD 的分发队列,GCD 会为你排期运行。
GCD 创建,重用,销毁线程,基于系统资源以它认为合适的方式运行每个队列。所以,用户需要关心的细节并不多。

GCD 的使用也很简单,假设抢面包是个耗时操作,前面例子中的 Geroge 和 Totty 的工作都可以实现如下:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
 // 并发队列中做耗时操作
[objc] view plain copy
在CODE上查看代码片派生到我的代码片
  1. while (TRUE) {  
  2. if(_cake > 0) {  
  3. // 耗时操作  
  4. [NSThread sleepForTimeInterval:0.5];  
  5.   _cake--;  
  6. _occupied = kSum - _cake;  
  7. else {  
  8. break;  
  9. }}  
  10.   
  11. // 主队列中刷新界面  
  12. dispatch_async(dispatch_get_main_queue(), ^{  
  13. [self updateUI];  
  14.     });  
  15. });  
GCD 分发队列

GCD分发队列是执行任务的有力工具。使用分发队列,你可以异步或者阻塞执行任意多个 block 的代码。
你可以使用分发队列来执行几乎任何线程任务。GCD 提供了简单易用的接口。

在 GCD 中存在三种队列:
1. 串行分发队列(Serial dispatch queue)
串行分发队列又被称为私有分发队列,按顺序执行队列中的任务,且同一时间只执行一个任务。串行分发队列常用于实现同步锁。
下面代码创建了一个串行分发队列:
dispatch_queue_t serialQueue = dispatch_queue_create("com.example.MyQueue", NULL);
2. 并发分发队列(Concurrent dispatch queue)
串行分发队列又被称为全局分发队列,也按顺序执行队列中的任务,但是顺序开始的多个任务会并发同时执行。
并发分发队列常用于管理并发任务。下面代码创建了一个并发分发队列:
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
3. 主分发队列(Main dispatch queue)
主分发队列是一个全局唯一的特殊的串行分发队列。队列中的任务会被在应用的主线程中执行。主分发队列可以用于执行 UI 相关的操作。
取得主分发队列的方法:
dispatch_queue_t mainQueue = dispatch_get_main_queue();

GCD 任务执行方式
GCD 中有两种任务执行方式:
异步执行, dispatch_async,意味将任务放入队列之后,主线程不会等待 block 的返回结果,而是立即继续执行下去。
阻塞执行, dispatch_sync,意味将任务放入队列之后,主线程被阻塞,需要等待 block 的执行结果返回,才能继续执行去。

GCD 的其他主题
GCD 有着丰富的功能,比如分发组(dispatch group),信号(semaphores),分发栅栏(dispatch barrier),分发源
(dispatch source)等等。这些可以用于完成更复杂的多线程任务。详细可以查阅 Apple 关于 GCD 的文档。
总结
在能够使用 GCD 的地方,尽量使用 GCD,在需要更细粒度控制线程时,考虑 NSOperation。
Apple 公司宣称其在 GCD 技术中为更好地利用多核硬件系统做了很多的优化。所以,在性能方面 GCD 是不用担心的。而且 
GCD 也提供了相当丰富的 API,几乎可以完成绝大部分线程相关的编程任务。
所以,在多线程相关主题的编程中,GCD 应该是首选。
下面举一些可以推荐使用 GCD 的实际例子:
1. 使用 GCD 的 dispatch queue 实现同步锁
同步锁的实现方案有不少,比如,如果仅仅是想对某个实例变量的读写操作加锁,可以使用属性(property)的 atomic 参数,
对于一段代码加锁可以使用 @synchronized 块,或者 NSLock。@synchronized 和 NSLock 实现的同步锁:
[objc] view plain copy
在CODE上查看代码片派生到我的代码片
  1. // Method 1  
  2. - (void)synchronizedMethod {  
  3.     @synchronized(self) {  
  4.         // safe  
  5.     }  
  6. }  
  7. // Method 2  
  8. _lock = [[NSLock alloc] init];  
  9. - (void)synchronizedMethod {  
  10.     [_lock lock];  
  11.     // Safe  
  12.     [_lock unlock];  
  13. }  
  14. @synchronized  
一般会以 self 为同步对象。重复调用 @synchronized(self) 是很危险的。
如果多个属性这么做,每一个属性将会被和其它所有属性同步,这可能并不是你所希望的,更好的方法是每个属性的锁都是相互独立的。
另一种方法是使用 NSLock 实现同步锁,这个方法不错,
但是缺点是在极端环境下同步块可能会导致锁死,而且这种情况下处理锁死状态会有麻烦。
一个替代方法是使用 GCD 的分发队列。
将读和写分发到相同并发队列中,这样读操作会是并发的,多个线程可以同时执行写操作;
而对于写操作,以分发栅栏(dispatch barrier)保证同时只有一个线程可以执行写操作,并且由于写操作无需返回,写操作还是异步马上返回的。
这样,就得到了一个高效且线程安全的锁。代码看起来会像这样:
[objc] view plain copy
在CODE上查看代码片派生到我的代码片
  1. _syncQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);  
  2. - (NSInteger)cake {  
  3.     __block NSInteger localCake;  
  4.     dispatch_sync(_syncQueue, ^{  
  5.         localCake = _cake;  
  6.     });  
  7.     return localCake;  
  8. }  
  9. - (void)setCake:(NSInteger)cake {  
  10.     dispatch_barrier_async(_syncQueue, ^{  
  11.         _cake = cake;  
  12.   });  
  13. }  

简单而言,上面的代码可以使读操作被竞争执行;写操作被互斥执行,并且异步返回。
使用 GCD 实现的这个同步锁应该是效率最优且最安全的。
2. 使用 GCD 替代 performSelector 系列方法
NSObject 的 performSelector 系列方法有很多限制。
传给要执行的方法的参数的数量是有限制的,也没法方法保证能正确地取得要执行的方法的返回值。
这些限制在使用 block 的 GCD 中都不存在。

下面是使用 GCD 替代 performSelector 的例子。使用 performSelector 系列方法:
[self performSelector:@selector(cake) withObject:nil afterDelay:5.0];
使用 GCD 完成相同的事情:
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC));
    dispatch_after(time, dispatch_get_main_queue(), ^(void){
    [self cake];
});

3. 使用 dispatch_once 实现线程安全单一执行要求
线程安全单一执行典型例子是单例,GCD 的 dispatch_once 能够保证传入的 block 被线程安全地唯一执行:
[objc] view plain copy
在CODE上查看代码片派生到我的代码片
  1. + (id)sharedInstance {  
  2.     static AdivseDemoController *sharedInstance = nil;  
  3.     static dispatch_once_t onceToken = @"token";  
  4.     dispatch_once(&onceToken, ^{  
  5.         sharedInstance = [[self alloc] init];  
  6.     });  
  7.     return sharedInstance;  
  8. }  
这是现在 Objective-C 中实现单例较为推荐的一种方法。
在需要更细粒度控制线程时,考虑 NSOperation
GCD 虽然在很多地方值得提倡,但并不是任务管理和多线程地唯一解决方案,并不是说所有的地方都应该使用 GCD。
GCD 是一个纯 C API,NSOperation 是 Objective-C 类,在一些地方对象编程是有优势的。
NSOperation 也提供了一些 GCD 无法实现,或者 GCD 所没有的功能。以下是你需要考虑使用 NSOperation 的一些理由:
当你需要取消线程任务时,GCD 无法提供取消任务的操作。而 NSOperation 提供了取消任务的操作;

当你需要更细的粒度地观察任务改变了状态时,由于 NSOperation 是一个对象,比较 GCD 使用的 block 而言,通过对 NS
Operation 对象进行键值观察(KVO)能很容易观察到任务的状态改变;

当你需要重用线程任务时,NSOperation 作为一个普通的 Objective-C 对象,可以存储任何信息。
对象就是为重用而设计的,这时,NSOperation 比 GCD 使用的 block 要更方便。

4.dispatch_after 延时执行
功能:延迟一段时间把一项任务提交到队列中执行,返回之后就不能取消
常用来在在主队列上延迟执行一项任务
函数原型
[objc] view plain copy
在CODE上查看代码片派生到我的代码片
  1. func dispatch_after(_ when: dispatch_time_t,  
  2.                   _ queue: dispatch_queue_t!,  
  3.                   _ block: dispatch_block_t!)  
参数
        when 过了多久执行的时间间隔
queue 提交到的队列
block 执行的任务
例如:3秒后将指定的Block 追加奥 Main Dispatch Queue 中。
[objc] view plain copy
在CODE上查看代码片派生到我的代码片
  1. dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 333ull * NSEC_PER_SEC);    
  2. dispatch_after(time, dispatch_get_main_queue(), ^{ NSLog(@“waited at least three seconds.”); });  

注: dispatch_after 函数并不是在指定时间后执行处理,而是在指定时间后追加处理到Dispatch Queue。
例如Main Dispatch Queue在主线程的RunLoop中执行。
所以在比如每隔1/60秒执行的RunLoop,Block最快在3秒后执行,最慢在 3+1/60秒后执行。

拓展:几种执行延迟线程的方法
1>最直接的方法performSelector:withObject:afterDelay:
这种方法的缺点:每次要为延时写一个方法

2>使用类别,用BOLCK执行
[objc] view plain copy
在CODE上查看代码片派生到我的代码片
  1. @implementation NSObject (PerformBlockAfterDelay)  
  2.        
  3.   - (void)performBlock:(void (^)(void))block  
  4.         afterDelay:(NSTimeInterval)delay  
  5.     {  
  6.         block = [[block copy] autorelease];  
  7.     [self performSelector:@selector(fireBlockAfterDelay:)  
  8.                withObject:block  
  9.                afterDelay:delay];  
  10.     }   
  11.   - (void)fireBlockAfterDelay:(void (^)(void))block {  
  12.         block();  
  13.     }  
  14.  @end  
3>使用GCD
[objc] view plain copy
在CODE上查看代码片派生到我的代码片
  1. void RunBlockAfterDelay(NSTimeInterval delay, void (^block)(void))  
  2.     {  
  3.         dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC*delay),  
  4.           dispatch_get_current_queue(), block);  
  5.     }  
4>使用animation的completion参数(不推荐)
[objc] view plain copy
在CODE上查看代码片派生到我的代码片
  1. [UIView animateWithDuration:0.0 delay:5.0 options:UIViewAnimationOptionAllowUserInteraction animations:^{  
  2.     } completion:^(BOOL finished) {  
  3.         //do stuff here  
  4. }];  
5>使用NSOperationQueue,在应用程序的下一个主循环执行:
[objc] view plain copy
在CODE上查看代码片派生到我的代码片
  1. [[NSOperationQueue mainQueue] addOperationWithBlock:aBlock];  
这个和调用performSelector: with afterDelay of 0.0f等价
5.dispatch_apply
功能:把一项任务提交到队列中多次执行,具体是并行执行还是串行执行由队列本身决定.注意,dispatch_apply不会立刻返回,在执行完毕后才会返回,是同步的调用。
func dispatch_apply(_ iterations: UInt,
                  _ queue: dispatch_queue_t!,
                  _ block: ((UInt) -> Void)!)
参数
iterations 执行的次数
queue 提交到的队列
block 执行的任务

那么,何时使用这个函数呢?
从它的功能不难看出,如果我们可以把不相关的循环提交到后台线程并行执行,并且循环任务调度到后台执行的效率提高,
能抵消掉队列调度本身的开销,那么效率会显著提高。

dispathc_apply 是dispatch_sync 和dispatch_group的关联API.它以指定的次数将指定的Block加入到指定的队列中。并等待队列中操作全部完成.
[objc] view plain copy
在CODE上查看代码片派生到我的代码片
  1. NSArray *array = [NSArray arrayWithObjects:@"/Users/chentao/Desktop/copy_res/gelato.ds",  
  2.                      @"/Users/chentao/Desktop/copy_res/jason.ds",  
  3.                      @"/Users/chentao/Desktop/copy_res/jikejunyi.ds",  
  4.                      @"/Users/chentao/Desktop/copy_res/molly.ds",  
  5.                      @"/Users/chentao/Desktop/copy_res/zhangdachuan.ds",  
  6.                      nil nil];  
  7.    NSString *copyDes = @"/Users/chentao/Desktop/copy_des";  
  8.    NSFileManager *fileManager = [NSFileManager defaultManager];  
  9.    dispatch_async(dispatch_get_global_queue(00), ^(){  
  10.        dispatch_apply([array count], dispatch_get_global_queue(00), ^(size_t index){  
  11.            NSLog(@"copy-%ld", index);  
  12.            NSString *sourcePath = [array objectAtIndex:index];  
  13.            NSString *desPath = [NSString stringWithFormat:@"%@/%@", copyDes, [sourcePath lastPathComponent]];  
  14.            [fileManager copyItemAtPath:sourcePath toPath:desPath error:nil];  
  15.        });  
  16.        NSLog(@"done");  
  17.    });  
输出 copy-index 顺序不确定,因为它是并行执行的(dispatch_get_global_queue是并行队列),但是done是在以上拷贝操作完成后才会执行,
因此,它一般都是放在dispatch_async里面(异步)。
实际上,这里 dispatch_apply如果换成串行队列上,则会依次输出index,但这样违背了我们想并行提高执行效率的初衷。
6.Dispatch Group
如果想要在追加到多个Dispatch Queue中的多个处理全部结束后执行结束处理,可使用Dispatch Group。
例如:
[objc] view plain copy
在CODE上查看代码片派生到我的代码片
  1. dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);    
  2. dispatch_group_t group = dispatch_group_create();    
  3.     
  4. dispatch_group_async(group, queue, ^{NSLog(@“bll0”);});    
  5. dispatch_group_async(group, queue, ^{NSLog(@“bll1”);});    
  6. dispatch_group_async(group, queue, ^{NSLog(@“bll2”);});    
  7.     
  8. dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@“done”); });    
  9. dispatch_release(group);    
  10.   
  11. 其中可以使用 dispatch_group_wait函数仅等待全部处理执行结束:  
  12. [objc] view plain copy 在CODE上查看代码片派生到我的代码片  
  13. dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);    
  14. dispatch_group_t group = dispatch_group_create();    
  15.     
  16. dispatch_group_async(group, queue, ^{NSLog(@“bll0”);});    
  17. dispatch_group_async(group, queue, ^{NSLog(@“bll1”);});    
  18. dispatch_group_async(group, queue, ^{NSLog(@“bll2”);});    
  19.     
  20. dispatch_group_wait(group, DISPATCH_TIME_FOREVER);    
  21. dispatch_release(group);     
注:dispatch_group_wait 的返回值为0,标识全部处理执行结束;
返回值不为0,表示经过了指定时间,属于Dispatch Group 的某一个处理还在执行中;
由于 DISPATCH_TIME_FOREVER 标识永久等待,故而返回值恒为0;

dispatch_group_wait 可能会造成当前线程停止,直至所有处理执行结束。

0 0