深入理解Dispatch Queue(一)


        Dispatch Queues提供了一种简单的方式让我们在App中实现异步和并发。任务的概念:一个任务是我们的程序需要执行的一个独立的工作。我们可以通过将相应的代码放到一个函数或一个block中并将其添加到调度队列中来定义任务。可以将调度队列理解为一个管理我们提交过的任务的对象。调度队列遵循先入先出的规则,提交到队列中的任务总是以相同的顺序开始启动的。

GCD为我们提供了三种调度队列
     
Concurrent queues (并发队列,也称为全局队列)
     按照任务加入队列的顺序同时执行一个或多个任务,任务的启动顺序仍是按照加入队列的顺序启动的。同时执行的任务执行在不同的线程之上由调度队列管理。同时执行的任务数目(使用的线程数)取决于系统的条件。系统为我们提供了四种预定义的全局并发调度队列。此外,在iOS5之后我们可以通过指定dispatch_queue_concurrent的队列类型来创建并发调度队列。

系统为我们提供了的四个并发调度队列

这四个队列的作用域是整个应用程序全局的。它们的区别仅仅在于它们的优先级。我们通过调用dispatch_get_global_queue拿到对应的队列。

    dispatch_queue_t globelQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

我们可以通过传递不同的参数获取不同优先级的全局队列
DISPATCH_QUEUE_PRIORITY_HIGH  高优先级
DISPATCH_QUEUE_PRIORITY_LOW  低优先级
DISPATCH_QUEUE_PRIORITY_BACKGROUND  后台运行队列
DISPATCH_QUEUE_PRIORITY_DEFAULT 默认优先级

对于dispatch_get_global_queue的第二个参数是用来对未来的扩展用的,现在应该总是传0。

虽然dispatch queues是引用计数对象,但我们不需要保留全局并发队列的引用或对全局队列进行retain或release操作。因为它们是全局的,保留和retain或release操作这些队列的调用将被忽略。因此,我们不需要存储这些队列的引用。当年需要它们的时候调用dispatch_get_global_queue。

Serial queues(串行队列)
     按照任务加入队列的顺序一次只执行一个任务。串行队列中的任务同样可能会运行在不同的线程上,但串行队列会保证任务会是被顺序执行的(当执行完一个任务才会去执行下一个)。

 创建串行调度队列

串行队列用于当我们期望任务以一个特定的顺序执行时。我们可以使用串行队列而不是锁来保护共享资源或可变数据结构。与锁不同的是串行队列在可预测的顺序中执行,而且队列永远不会死锁。

不像并行队列,我们必须显示的创建和管理自己的串行队列。你可以创建任意数量的串行队列,但是要避免创建大量的串行队列,只作为一种手段来同时执行尽可能多的任务。如果你想同时执行大量的任务,将它们提交到全局并发队列中。

自定义串行队列的创建:

    dispatch_queue_t serialQueue = dispatch_queue_create("com.example.SerialQueue", NULL);

dispatch_queue_create有两个参数第一个参数为队列名称,主要作用是帮助调试。第二个参数是保留为将来使用的现在应该总是传NULL。

除了我们创建的自定义串行队列外,系统自动地创建了一个串行队列,并将其与应用程序的主线程绑定即为主调度队列。


Main dispatch queue(主调度队列)
     主调度队列是一个全局可用的串行队列,主调度队列中的任务会在主线程中执行。在应用程序的主运行循环中,程序会交替的执行主调度队列中的任务和其他事件源的任务。由于主调度队列运行在主线程中,所以主调度队列通常会用做应用程序的一个关键的同步点。虽然我们不必创建主调度队列,但是我们的确需要确保在我们的程序中合理的使用它。我们可以通过调用dispatch_get_main_queue方法来获取主调度队列。

 向队列中添加任务

     将任务添加到队列中有两种方法:异步或同步。在两者均可的情况下,异步方案要优于同步方案。可以使用dispatch_async和dispatch_async_f函数来添加异步任务。
     
     虽然大多数情况下我们使用异步的方式来添加任务,但是我们有时会需要同步的任务来处理一些类似于资源共享之类的问题。这时,可以使用 dispatch_syncdispatch_sync_f来添加同步任务。此时任务代码会在当前线程得到执行。
    
关于串行队列、并行队列,同步方法、异步方法的使用可能容易产生迷惑,下面有两个简单的例子
 示例1:
      -(void)testForGCDWithGlobelQueue{
    dispatch_queue_t globelQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
   
   
dispatch_async(globelQueue, ^{
       
//        for (int i = 0; i < 1000000000; i ++) {
       
//
       
//        }
       
NSLog(@"Do some work hear on  %@",[NSThread currentThread]);
    });
      
    dispatch_sync(globelQueue, ^{
       
NSLog(@"Do some more work here on %@",[NSThread currentThread]);
    });
   
   
NSLog(@"seconed blocks have completed");
}
执行结果:
    2016-06-13 16:19:59.433 GCD[2322:195956] Do some work hear on  <NSThread: 0x7fc201d17c20>{number = 2, name = (null)}
    2016-06-13 16:19:59.434 GCD[2322:195849] Do some more work here on <NSThread: 0x7fc201e01fd0>{number = 1, name = main}
    2016-06-13 16:19:59.434 GCD[2322:195849] seconed blocks have completed

而如果我们将testForGCDWithGlobelQueue中对耗时操作(for循环)的注释打开,我们会得到这样的运行结果:
    2016-06-13 16:23:48.392 GCD[2349:198380] Do some more work here on <NSThread: 0x7f8183500550>{number = 1, name = main}
    2016-06-13 16:23:48.394 GCD[2349:198380] seconed blocks have completed
    2016-06-13 16:23:51.303 GCD[2349:198424] Do some work hear on  <NSThread: 0x7f81834698a0>{number = 2, name = (null)}

示例2:
      -(void)testForGCDWithSerialQueue{
   
dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", NULL);
   
   
dispatch_async(serialQueue, ^{
//        for (int i = 0; i < 1000000000; i ++) {
//       
//        }
       
NSLog(@"Do some work hear on  %@",[NSThread currentThread]);
    });
   
   
dispatch_sync(serialQueue, ^{
       
NSLog(@"Do some more work here on %@",[NSThread currentThread]);
    });
   
   
NSLog(@"Both blocks have completed");
}
执行结果:
     2016-06-13 16:32:36.828 GCD[2439:204353] Do some work hear on  <NSThread: 0x7fb3a9d89940>{number = 2, name = (null)}
   2016-06-13 16:32:36.828 GCD[2439:204212] Do some more work here on <NSThread: 0x7fb3a9c04380>{number = 1, name = main}
   2016-06-13 16:32:36.829 GCD[2439:204212] Both blocks have completed

而如果我们将testForGCDWithSerialQueue中对耗时操作(for循环)的注释打开,我们会得到这样的运行结果:
   2016-06-13 16:34:23.568 GCD[2448:205195] Do some work hear on  <NSThread: 0x7fa218e11e80>{number = 2, name = (null)}
   2016-06-13 16:34:23.569 GCD[2448:205118] Do some more work here on <NSThread: 0x7fa218c029e0>{number = 1, name = main}
   2016-06-13 16:34:23.569 GCD[2448:205118] Both blocks have completed

总结:
     串行队列、并行队列的作用是确定任务可同时执行的数量。
     同步方法、异步方法的作用是执行该任务是否开启新的线程。
     所以串行队列里的任务也可能会在不同的线程内执行,只有一个任务在执行。
     

     调度队列中的任务是独立运行的代码。然而,当任务完成的时候应用程序可能需要监测任务的完成进而执行一些必要的操作。传统的异步操作中可以通过回调来解决这个问题。在GCD中我们通过在代码执行完成后添加一个完成block来实现。
     其基本的代码组织逻辑是这样的:
      -(void)testForComplishBlock{
   
dispatch_queue_t globelQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
   
   
dispatch_async(globelQueue, ^{
       
NSLog(@" Some time-consuming operations such as download pictures");
       
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@“Update UI or other operations");      
        });
    });
}
      这也是GCD最常用的代码组织方式,在子线程进行耗时操作,在主线程更新UI。
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值