GCD 线程

GCD(Grand Central Dispatch)。

iOS4.0中首度引入GCD,GCD是管理任务执行的一项技术,它使得我们对多任务处理变得更加方便和有效。它支持同步或异步任务处理,串行或并行的处理队列(Dispath Queue),非系统调用的信号量机制,定时任务处理,进程、文件或网络的监听任务等。这个庞大的任务处理技术大大减少了线程的管理工作,使基于任务的开发变得更加高效。


Dispatch Queue是一个任务执行队列,可以让你异步或同步地执行多个Block或函数。Dispatch Queue是FIFO的,即先入队的任务总会先执行。目前有三种类型的Dispath Queue:

Dispatch Queue

1.主队列(Main dispatch queue)

2.串行队列(Serial dispatch queue)

3.并发队列(Concurrent dispatch queue)


主队列

主队列是一个特殊的队列,它是系统预定义的运行在主线程的一个Dispatch Queue。可以通过dispatch_get_main_queue来获取唯一的主队列。主队列一般运行一些需要与主线程同步的一些短时任务,例子:

<span style="font-family:SimSun;font-size:14px;">//GCD下载图片刷新主界面的例子  
/* 
- (IBAction)touchUpInsideByThreadOne:(id)sender { 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
        NSURL * url = [NSURL URLWithString:@"http://avatar.csdn.net/2/C/D/1_totogo2010.jpg"]; 
        NSData * data = [[NSData alloc]initWithContentsOfURL:url]; 
        UIImage *image = [[UIImage alloc]initWithData:data]; 
        if (data != nil) { 
            dispatch_async(dispatch_get_main_queue(), ^{ 
                self.imageView.image = image; 
            }); 
        } 
    }); 
}*/  </span>

通过与线程池的配合,dispatch queue分为下面两种:而系统默认就有一个 串行队列 main_queue 和 并行队列 global_queue:

  •      Serial Dispatch Queue -- 线程池只提供一个线程用来执行任务,所以后一个任务必须等到前一个任务执行结束才能开始。
  •      Concurrent Dispatch Queue -- 线程池提供多个线程来执行任务,所以可以按序启动多个任务并发执行。

而系统默认就有一个串行队列main_queue和并行队列global_queue:

dispatch_queue_t globalQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);  
dispatch_queue_t mainQ = dispatch_get_main_queue(); 


通常,我们可以在global_queue中做一些long-running的任务,完成后在main_queue中更新UI,避免UI阻塞,无法响应用户操作:


<span style="font-size:14px;">dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{  
        // long-running task  
        dispatch_async(dispatch_get_main_queue(), ^{  
            // update UI  
        });  
    });</span>


串行队列

串行队列一次只能处理一个任务,可以由用户调用dispatch_queue_create创建:

<span style="font-size:14px;">dispatch_queue_t queue; 
queue = dispatch_queue_create("com.example.MyQueue", NULL); </span>

dispatch_queue_create第一个参数是串行队列标识,一般用反转域名的格式表示以防冲突;第二个参数是queue的类型,设为NULL时默认是DISPATCH_QUEUE_SERIAL,将创建串行队列,在必要情况下,你可以将其设置为DISPATCH_QUEUE_CONCURRENT来创建自定义并行队列。

下面是图片下载的例子:

<span style="font-size:14px;">- (IBAction)touchUpInsideByThreadOne:(id)sender {  
    NSDate *da = [NSDate date];  
    NSString *daStr = [da description];  
    const char *queueName = [daStr UTF8String];  
    dispatch_queue_t myQueue = dispatch_queue_create(queueName, NULL);  
      
    dispatch_async(myQueue, ^{  
        NSURL * url = [NSURL URLWithString:@"http://avatar.csdn.net/2/C/D/1_totogo2010.jpg"];  
        NSData * data = [[NSData alloc]initWithContentsOfURL:url];  
        UIImage *image = [[UIImage alloc]initWithData:data];  
        if (data != nil) {  
            dispatch_async(dispatch_get_main_queue(), ^{  
                self.imageView.image = image;  
            });  
        }  
    });  
      
    dispatch_release(myQueue);  
}  </span>

为了验证Serial queue的FIFO特性,写了如下的验证代码:发现的确是顺序执行的。

<span style="font-size:14px;">- (IBAction)touchUpInsideByThreadOne:(id)sender {  
    NSDate *da = [NSDate date];  
    NSString *daStr = [da description];  
    const char *queueName = [daStr UTF8String];  
    dispatch_queue_t myQueue = dispatch_queue_create(queueName, DISPATCH_QUEUE_SERIAL);  
      
    dispatch_async(myQueue, ^{  
        [NSThread sleepForTimeInterval:6];  
        NSLog(@"[NSThread sleepForTimeInterval:6];");  
    });  
      
    dispatch_async(myQueue, ^{  
        [NSThread sleepForTimeInterval:3];  
        NSLog(@"[NSThread sleepForTimeInterval:3];");  
    });  
      
    dispatch_async(myQueue, ^{  
        [NSThread sleepForTimeInterval:1];  
        NSLog(@"[NSThread sleepForTimeInterval:1];");  
    });  
      
    dispatch_release(myQueue);  
}  </span>

运行结果为:
<span style="font-size:14px;">2013-07-24 16:37:14.397 NSThreadAndBlockDemo[1924:12303] [NSThread sleepForTimeInterval:6];  
2013-07-24 16:37:17.399 NSThreadAndBlockDemo[1924:12303] [NSThread sleepForTimeInterval:3];  
2013-07-24 16:37:18.401 NSThreadAndBlockDemo[1924:12303] [NSThread sleepForTimeInterval:1]; </span> 


并发队列

并行队列可以同时处理多个任务,在不得以的情况下可以用dispatch_queue_create创建,但一般我们都要用系统预定义的并行队列,即全局队列(Global Concurrent Dispatch Queues)。目前系统预定义了四个不同运行优先级的全局队列,我们可以通过dispatch_get_global_queue来获取它们。

dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 

dispatch_get_global_queue第一个参数是队列的优先级,分别对应四个全局队列:

DISPATCH_QUEUE_PRIORITY_HIGH     

DISPATCH_QUEUE_PRIORITY_DEFAULT

DISPATCH_QUEUE_PRIORITY_LOW

DISPATCH_QUEUE_PRIORITY_BACKGROUND

 

dispatch_get_global_queue中第二个参数目前系统保留,请设置为0即可。

例子:

<span style="font-size:14px;">- (IBAction)touchUpInsideByThreadOne:(id)sender {  
    dispatch_queue_t myQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);  
      
    dispatch_async(myQueue, ^{  
        [NSThread sleepForTimeInterval:6];  
        NSLog(@"[NSThread sleepForTimeInterval:6];");  
    });  
      
    dispatch_async(myQueue, ^{  
        [NSThread sleepForTimeInterval:3];  
        NSLog(@"[NSThread sleepForTimeInterval:3];");  
    });  
      
    dispatch_async(myQueue, ^{  
        [NSThread sleepForTimeInterval:1];  
        NSLog(@"[NSThread sleepForTimeInterval:1];");  
    });  
      
    dispatch_release(myQueue); </span> 

运行的结果为:

<span style="font-size:14px;">2013-07-24 16:38:41.660 NSThreadAndBlockDemo[1944:12e03] [NSThread sleepForTimeInterval:1];  
2013-07-24 16:38:43.660 NSThreadAndBlockDemo[1944:12b03] [NSThread sleepForTimeInterval:3];  
2013-07-24 16:38:46.660 NSThreadAndBlockDemo[1944:12303] [NSThread sleepForTimeInterval:6]; </span>

Dispatch Queue的内存管理

除了系统预定义的Dispatch Queue,我们自定义的Dispatch Queue需要手动的管理它的内存。

dispatch_retaindispatch_release这两个函数可以控制Dispatch Queue的引用计数(同时可以控制后面会讲到的Dispatch Group和Dispatch Source的引用计数)。当Dispatch Queue引用计数变为0后,就会调用finalizer,finalizer是Dispatch Queue销毁前调用的函数,用来清理Dispatch Queue的相关资源。可以用dispatch_set_finalizer_f函数来设置Dispatch Queue的finalizer,这个函数同时可以设置Dispatch Group和Dispatch Source的销毁函数。

void dispatch_set_finalizer_f(dispatch_object_t object, dispatch_function_t finalizer); 


下边是一些常用到的函数:

1.  dispatch_group_async的使用

dispatch_group_async可以实现监听一组任务是否完成,完成后得到通知执行其他的操作。这个方法很有用,比如你执行三个下载任务,当三个任务都下载完成后你才通知界面说完成的了。下面是一段例子代码:

<span style="font-size:14px;">- (IBAction)touchUpInsideByThreadOne:(id)sender {  
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);  
    dispatch_group_t group = dispatch_group_create();  
    dispatch_group_async(group, queue, ^{  
        [NSThread sleepForTimeInterval:6];  
        NSLog(@"group1 [NSThread sleepForTimeInterval:6];");  
    });  
    dispatch_group_async(group, queue, ^{  
        [NSThread sleepForTimeInterval:3];  
        NSLog(@"group2 [NSThread sleepForTimeInterval:3];");  
    });  
    dispatch_group_async(group, queue, ^{  
        [NSThread sleepForTimeInterval:1];  
        NSLog(@"group3 [NSThread sleepForTimeInterval:1];");  
    });  
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{  
        NSLog(@"main thread.");  
    });  
    dispatch_release(group);  
}  </span>

执行结果为:
<span style="font-size:14px;">2013-07-24 16:48:23.063 NSThreadAndBlockDemo[2004:12e03] group3 [NSThread sleepForTimeInterval:1];  
2013-07-24 16:48:25.063 NSThreadAndBlockDemo[2004:12b03] group2 [NSThread sleepForTimeInterval:3];  
2013-07-24 16:48:28.063 NSThreadAndBlockDemo[2004:12303] group1 [NSThread sleepForTimeInterval:6];  
2013-07-24 16:48:28.065 NSThreadAndBlockDemo[2004:11303] main thread.</span>

果然, dispatch_group_async只会监听最终的结果完成后,并通知main queue,那如果是我们需要顺序执行的话呢?请看下面的dispatch_barrier_async。


2.  dispatch_barrier_async的使用

dispatch_barrier_async是在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行

例子代码如下:

<span style="font-size:14px;">- (IBAction)touchUpInsideByThreadOne:(id)sender {  
    dispatch_queue_t queue = dispatch_queue_create("gcdtest.rongfzh.yc", DISPATCH_QUEUE_CONCURRENT);  
      
    dispatch_async(queue, ^{  
        [NSThread sleepForTimeInterval:3];  
        NSLog(@"dispatch_async1");  
    });  
    dispatch_async(queue, ^{  
        [NSThread sleepForTimeInterval:1];  
        NSLog(@"dispatch_async2");  
    });  
    dispatch_barrier_async(queue, ^{  
        NSLog(@"dispatch_barrier_async");  
        [NSThread sleepForTimeInterval:0.5];  
          
    });  
    dispatch_async(queue, ^{  
        [NSThread sleepForTimeInterval:1];  
        NSLog(@"dispatch_async3");  
    });  
}  </span>
执行结果为:
<span style="font-size:14px;">2013-07-24 17:01:54.580 NSThreadAndBlockDemo[2153:12b03] dispatch_async2  
2013-07-24 17:01:56.580 NSThreadAndBlockDemo[2153:12303] dispatch_async1  
2013-07-24 17:01:56.580 NSThreadAndBlockDemo[2153:12303] dispatch_barrier_async  
2013-07-24 17:01:58.083 NSThreadAndBlockDemo[2153:12303] dispatch_async3 </span> 

如果使用dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);会发现运行结果为:
<span style="font-size:14px;">2013-07-24 17:07:17.577 NSThreadAndBlockDemo[2247:12e03] dispatch_barrier_async  
2013-07-24 17:07:18.579 NSThreadAndBlockDemo[2247:15207] dispatch_async3  
2013-07-24 17:07:19.578 NSThreadAndBlockDemo[2247:12b03] dispatch_async2  
2013-07-24 17:07:20.577 NSThreadAndBlockDemo[2247:12303] dispatch_async1 </span>
说明dispatch_barrier_async的顺序执行还是依赖queue的类型啊,必需要queue的类型为dispatch_queue_create创建的,而且attr参数值必需是DISPATCH_QUEUE_CONCURRENT类型,前面两个非dispatch_barrier_async的类型的执行是依赖其本身的执行时间的,如果attr如果是DISPATCH_QUEUE_SERIAL时,那就完全是符合Serial queue的FIFO特征了。


3.  dispatch_apply

执行某个代码片段N次。

dispatch_apply(5, globalQ, ^(size_t index) {

// 执行5次

});

5.  dispatch_once

  dispatch_once这个函数,它可以保证整个应用程序生命周期中某段代码只被执行一次

<span style="font-family:SimSun;"><span style="font-size:14px;">double delayInSeconds = 2.0;  
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);  
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){  
        // code to be executed on the main queue after delay  
    }); </span> </span>

6.  dispatch_set_target_queue

通过dispatch_set_target_queue函数可以设置一个dispatch queue的优先级,或者指定一个dispatch source相应的事件处理提交到哪个queue上。

改变queue的优先级:

// 改变serialQueue(Default Priority)优先级为Background Priority
dispatch_queue_t serialQueue = dispatch_queue_create("com.mark.serialQueue", NULL);
dispatch_queue_t globalDispatchQueueBackground = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_set_target_queue(serialQueue, globalDispatchQueueBackground);

7.  dispatch_suspend 和 dispatch_resume

// 挂起队列
dispatch_suspend(queue);
// 恢复队列执行
dispatch_resume(queue);

8.  dispatch_after  延迟

// 2秒后将指定的Block增加到指定的Dispatch Queue中
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
    NSLog(@"Waitted at least 2 seconds");
});






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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值