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

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)

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

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相集成。对象需要使用的任何事件或者计时器都可以从该对象的队列中指向,使得这些句柄可以自动在该队列上执行,从而使得句柄可以与对象自动同步。

  1. backlink tool 12月 29th, 2012 @ 21:39 | #1

    Needed to compose you this bit of observation so as to say thanks once again with the wonderful thoughts you have provided on this site. It is really unbelievably generous with people like you in giving easily just what many people could have offered for sale as an electronic book to earn some cash for themselves, especially considering that you might well have done it in case you decided. The inspiring ideas likewise served to be a good way to be aware that other individuals have the identical desire really like mine to find out a lot more when considering this condition. I believe there are many more enjoyable sessions in the future for folks who read through your blog post.

  2. maillot de foot 2013 4月 23rd, 2013 @ 04:54 | #2

    I’ll apply this idea…… It can be fun!

  3. cheap monster energy hats 4月 25th, 2013 @ 01:55 | #3

    Awesome Blog. I add this Post to my bookmarks.

评论提交中, 请稍候...

留言


可以使用的标签: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>
Trackbacks & Pingbacks ( 0 )
  1. 还没有 trackbacks