iOS系统GCD学习(2):Dispatch Queue

GCD的基本概念就是 dispatch queue 。dispatch queue是一个对象,它可以接受任务,并将任务以先到先执行的顺序来执行。dispatch queue可以是并发的或串行的。并发任务会像NSOperationQueue那样基于系统负载来合适地并发进行,串行队列同一时间只执行单一任务。

  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。Apple建议我们使用倒置域名来命名队列,比如“com.dreamingwish.subsystem.task”。这些名字会在崩溃日志中被显示出来,也可以被调试器调用,这在调试中会很有用。第二个参数目前还不支持,传入NULL就行了。

提交 Job

  向一个队列提交Job很简单:调用dispatch_async函数,传入一个队列和一个block。队列会在轮到这个block执行时执行这个block的代码。下面的例子是一个在后台执行一个巨长的任务:

 
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
        [self goDoSomethingLongAndInvolved]; 
        NSLog(@"Done doing something long and involved"); 
});

dispatch_async 函数会立即返回, block会在后台异步执行。

  当然,通常,任务完成时简单地NSLog个消息不是个事儿。在典型的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"]; 
        }); 
});

  还有一个函数叫dispatch_sync,它干的事儿和dispatch_async相同,但是它会等待block中的代码执行完成并返回。结合 __block类型修饰符,可以用来从执行中的block获取一个值。例如,你可能有一段代码在后台执行,而它需要从界面控制层获取一个值。那么你可以使用dispatch_sync简单办到:

__block NSString *stringValue; 
dispatch_sync(dispatch_get_main_queue(), ^{ 
        // __block variables aren't automatically retained 
        // so we'd better make sure we have a reference we can keep 
        stringValue = [[textField stringValue] copy]; 
}); 
[stringValue autorelease]; 
// use stringValue in the background now

  我们还可以使用更好的方法来完成这件事——使用更“异步”的风格。不同于取界面层的值时要阻塞后台线程,你可以使用嵌套的block来中止后台线程,然后从主线程中获取值,然后再将后期处理提交至后台线程:

dispatch_queue_t bgQueue = myQueue;
    dispatch_async(dispatch_get_main_queue(), ^{
        NSString *stringValue = [[[textField stringValue] copy] autorelease];
        dispatch_async(bgQueue, ^{
            // use stringValue in the background now
        });
});

取决于你的需求,myQueue可以是用户队列也可以使全局队列。

不再使用锁(Lock)

  用户队列可以用于替代锁来完成同步机制。在传统多线程编程中,你可能有一个对象要被多个线程使用,你需要一个锁来保护这个对象:

1
NSLock *lock;

  访问代码会像这样:

- (id)something 
{ 
    id localSomething; 
    [lock lock]; 
    localSomething = [[something retain] autorelease]; 
    [lock unlock]; 
    return localSomething; 
} 
   
- (void)setSomething:(id)newSomething 
{ 
    [lock lock]; 
    if(newSomething != something) 
    { 
        [something release]; 
        something = [newSomething retain]; 
        [self updateSomethingCaches]; 
    } 
    [lock unlock]; 
}

  使用GCD,可以使用queue来替代:dispatch_queue_t queue;要用于同步机制,queue必须是一个用户队列,而非全局队列,所以使用usingdispatch_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一样。现在你可能要问:“这样很好,但是有意思吗?我就是换了点代码办到了同一件事儿。”
  实际上,使用GCD途径有几个好处:

1.平行计算: 注意在第二个版本的代码中, -setSomething:是怎么使用dispatch_async的。调用 -setSomething:会立即返回,然后这一大堆工作会在后台执行。如果updateSomethingCaches是一个很费时费力的任务,且调用者将要进行一项处理器高负荷任务,那么这样做会很棒。
2.安全: 使用GCD,我们就不可能意外写出具有不成对Lock的代码。在常规Lock代码中,我们很可能在解锁之前让代码返回了。使用GCD,队列通常持续运行,你必将归还控制权。
3.控制: 使用GCD我们可以挂起和恢复dispatch queue,而这是基于锁的方法所不能实现的。我们还可以将一个用户队列指向另一个dspatch queue,使得这个用户队列继承那个dispatch queue的属性。使用这种方法,队列的优先级可以被调整——通过将该队列指向一个不同的全局队列,若有必要的话,这个队列甚至可以被用来在主线程上执行代码。
4.集成: GCD的事件系统与dispatch queue相集成。对象需要使用的任何事件或者计时器都可以从该对象的队列中指向,使得这些句柄可以自动在该队列上执行,从而使得句柄可以与对象自动同步。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值