了解 dispatch_async

Posted

技术标签:

【中文标题】了解 dispatch_async【英文标题】:Understanding dispatch_async 【发布时间】:2013-04-23 09:57:13 【问题描述】:

我对这段代码有疑问

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_queue 有什么优势?

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

【问题讨论】:

你最好把你的代码分成几行,这样更有意义。在dispatch_queue_t myQueue 的变量类型中保护您的dispatch_get_global_queue。仅将 myQueue 传递给您的 ``dispatch_async` 更具可读性 【参考方案1】:

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

例如,如果我正在从 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
    );
);

【讨论】:

我明白大卫感谢您的回答,但我的问题更多的是理解这样做的逻辑,即要求此代码在全局队列上执行串行任务,而全局队列本身就是并发队列 我正在按照您的建议做,但不知何故,当我在 Run UI Updates 中调用 [self.tableView reloadData] 时,uiTableViewCell 不会立即更新。大约需要 4 或 5 秒。几天来一直让我发疯。 @GrandSteph 我对这种方法不太熟悉。也许该方法只需要 5 秒即可运行。 dispatch_async 的重要之处在于它允许您在后台执行操作而无需挂起主线程。 0 表示什么? @Honey 0 是flags 参数,它目前完全没有任何作用。来自文档:Flags that are reserved for future use. Always specify 0 for this parameter.【参考方案2】:

所有 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(即您将最后 250 个任务安排在 HIGH 队列上),很可能您看到开始的第一个任务将在该 HIGH 队列中,因为系统已经暗示这些任务需要尽快到达 CPU。

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

根据苹果:

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

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

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

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

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

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

【讨论】:

+1,但我认为在实践中并发队列是 FIFO 还是只是随机顺序并不重要。如果您在一个循环中启动 5 个任务,假设它们基本上会同时启动。不能保证,例如第一个任务的第一个 I/O 操作将在第五个之前发生,即使它们执行相同的代码。 OTOH,对于串行队列,FIFO 行为是必不可少的,恕我直言,这是两种队列类型之间的定义差异。 难以置信的解释。鼓掌!【参考方案3】:

斯威夫特版本

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

DispatchQueue.global(qos: .background).async 
    
    // Background Thread
    
    DispatchQueue.main.async 
        // Run UI Updates
    

【讨论】:

以上是关于了解 dispatch_async的主要内容,如果未能解决你的问题,请参考以下文章

了解了解grpc

数据库 了解知识

深入了解ASP.NET运行内幕

了解GStreamer

深入了解Java的String

Vue UI 了解一下