了解dispatch_async


233

我对此代码有疑问

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSData* data = [NSData dataWithContentsOfURL: 
      kLatestKivaLoansURL];
    [self performSelectorOnMainThread:@selector(fetchedData:) 
      withObject:data waitUntilDone:YES];
});

此代码的第一个参数是

dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) 

我们是否在要求此代码在全局队列上执行串行任务,该队列本身的定义是返回给定优先级的全局并发队列?

使用dispatch_get_global_queuemain队列有什么好处?

我很困惑。您能否帮助我更好地理解这一点。


1
您最好将代码分成几行,这样才更有意义。确保您dispatch_get_global_queue的变量类型安全dispatch_queue_t myQueue。它更具可读性,仅将myQueue传递给您的“ dispatch_async”
Alex Cio,

Answers:


517

在主队列上使用默认队列的主要原因是在后台运行任务。

例如,如果我要从Internet下载文件,并且要向用户更新下载进度,则将在优先级默认队列中运行下载,并异步在主队列中更新UI。

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
    //Background Thread
    dispatch_async(dispatch_get_main_queue(), ^(void){
        //Run UI Updates
    });
});

我知道大卫非常感谢您的回答,但我的问题是更多地了解执行此操作的逻辑,即要求该代码在作为并发队列本身的全局队列上执行串行任务
user2332873 2013年

我正在按照您的建议做,但是以某种方式,当我在Run UI Updates中调用[self.tableView reloadData]时,uiTableViewCell不会立即更新。大约需要4或5秒钟。这已经让我发疯了好几天了。
GrandSteph

@GrandSteph我不太熟悉该方法。也许该方法只需要5秒钟即可运行。dispatch_async的重要之处在于,它允许您在不挂主线程的情况下在后台执行操作。
大卫

2
0意味着什么?
Honey

3
@Honey 0是flags参数,当前绝对不执行任何操作。来自文档:Flags that are reserved for future use. Always specify 0 for this parameter.
David

199

所有DISPATCH_QUEUE_PRIORITY_X队列都是并发队列(意味着它们可以一次执行多个任务),并且在给定队列中的任务将开始使用“先进先出”顺序执行的意义上是FIFO。这与主队列(来自dispatch_get_main_queue())比较,后者是一个串行队列(任务将按接收顺序开始执行并完成执行)。

因此,如果将1000个dispatch_async()块发送到DISPATCH_QUEUE_PRIORITY_DEFAULT,这些任务将按照将它们发送到队列的顺序开始执行。对于HIGH,LOW和Background队列也是如此。您发送到这些队列中任何队列中的任何内容都在备用线程中在后台执行,而与您的主应用程序线程无关。因此,这些队列适合执行诸如后台下载,压缩,计算等任务。

请注意,每个队列的执行顺序为FIFO。因此,如果您将1000个dispatch_async()任务发送到四个不同的并发队列,将它们平均分配,然后按顺序发送到Background,LOW,DEFAULT和HIGH(即,在HIGH队列中安排最后250个任务),则很可能您看到的开始的第一个任务将在HIGH队列中,因为系统暗示您这些任务需要尽快到达CPU。

还要注意,我说“将开始按顺序执行”,但要记住,并发队列不一定会根据每个任务的时间长短按顺序完成执行。

根据苹果:

https://developer.apple.com/library/content/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html

当您有多个可以并行运行的任务时,并发调度队列很有用。并发队列仍然是一个队列,因为它按照先进先出的顺序使任务出队。但是,并发队列可能在其他先前任务完成之前使其他任务出队。并发队列在任何给定时刻执行的实际任务数是可变的,并且可以随着应用程序条件的变化而动态变化。许多因素会影响并发队列执行的任务数量,包括可用核的数量,其他进程正在完成的工作量以及其他串行调度队列中任务的数量和优先级。

基本上,如果将这1000个dispatch_async()块发送到DEFAULT,HIGH,LOW或Background队列,它们将按照发送它们的顺序开始执行。但是,较短的任务可能会在较长的任务之前完成。造成这种情况的原因是,如果有可用的CPU内核,或者当前的队列任务是否正在执行非计算密集型工作(因此,使系统认为无论内核数如何,系统都可以并行调度其他任务)。

并发级别完全由系统处理,并基于系统负载和其他内部确定的因素。这是Grand Central Dispatch(dispatch_async()系统)的优点-您只需将工作单元设置为代码块,为它们设置优先级(基于您选择的队列),然后让系统处理其余部分。

因此,回答以上问题:您部分正确。您正在“询问该代码”以指定的优先级级别在全局并发队列上执行并发任务。该块中的代码将在后台执行,并且任何其他(类似)代码都可能并行执行,具体取决于系统对可用资源的评估。

另一方面,“ main”队列(来自dispatch_get_main_queue())是一个串行队列(非并发)。发送到主队列的任务将始终按顺序执行,并且将始终按顺序完成。这些任务也将在UI线程上执行,因此适合通过进度消息,完成通知等更新UI。


+1,但我认为实际上并发队列是FIFO还是随机顺序都没关系。如果您以循环方式启动5个任务,请假定它们实际上将同时启动。不能保证例如第一个任务的第一个I / O操作将在第5个任务之前发生,即使它们执行相同的代码。OTOH,对于串行队列,FIFO行为至关重要,恕我直言,这是两种队列类型之间的定义差异。
Gerhard Wesp 2015年

令人难以置信的解释。鼓掌很多!
Okhan Okbay

36

迅捷版

这是David的Objective-C答案的Swift版本。您可以使用全局队列在后台运行事物,并使用主队列来更新UI。

DispatchQueue.global(qos: .background).async {
    
    // Background Thread
    
    DispatchQueue.main.async {
        // Run UI Updates
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.