【问题标题】:Does a GCD dispatch_async wait on NSLog()?GCD dispatch_async 是否等待 NSLog()?
【发布时间】:2011-12-15 19:56:54
【问题描述】:

从我读到的关于 Grand Central Dispatch 的文章中,GCD 不做抢先式多任务处理;这都是一个单一的事件循环。我无法理解这个输出。我有两个队列只是在做一些输出(起初我正在读/写一些共享状态,但我能够简化到这个并且仍然得到相同的结果)。

dispatch_queue_t authQueue = dispatch_queue_create("authQueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t authQueue2 = dispatch_queue_create("authQueue", DISPATCH_QUEUE_SERIAL);

dispatch_async(authQueue, ^{ 
    NSLog(@"First Block");
    NSLog(@"First Block Incrementing"); 
    NSLog(@"First Block Incremented"); 
});

dispatch_async(authQueue, ^{ 
    NSLog(@"Second Block");
    NSLog(@"Second Block Incrementing");
    NSLog(@"Second Block Incremented"); 
});

dispatch_async(authQueue2,^{ 
    NSLog(@"Third Block"); 
    NSLog(@"Third Block Incrementing");
    NSLog(@"Third Block Incremented"); 
});

我得到以下输出:

2011-12-15 13:47:17.746 App[80376:5d03] Third Block
2011-12-15 13:47:17.746 App[80376:1503] First Block
2011-12-15 13:47:17.746 App[80376:5d03] Third Block Incrementing
2011-12-15 13:47:17.746 App[80376:1503] First Block Incrementing
2011-12-15 13:47:17.748 App[80376:1503] First Block Incremented
2011-12-15 13:47:17.748 App[80376:5d03] Third Block Incremented
2011-12-15 13:47:17.750 App[80376:1503] Second Block
2011-12-15 13:47:17.750 App[80376:1503] Second Block Incrementing
2011-12-15 13:47:17.751 App[80376:1503] Second Block Incremented

很明显,这些块不会自动执行。我唯一的理论是 GCD 通过 NSLog 写入 stdio 会使当前执行等待。我在 Apple 文档中找不到与此相关的任何内容。谁能解释一下?

【问题讨论】:

    标签: ios cocoa concurrency grand-central-dispatch nslog


    【解决方案1】:

    GCD 不使用任何类型的“事件循环”。这是最近发布的 Mac OS X 和 iOS 中的一项新内核功能,据我所知,它并没有任何其他类似的技术。

    目标是在硬件允许的范围内尽快完成执行您提供的所有代码。请注意,它的目标是最快的完成时间,而不是最快的开始时间。一个微妙的区别,但对它的工作方式具有现实世界影响的重要区别。

    如果您只有一个空闲的 CPU 内核,那么理论上一次只会执行其中一个。因为单核内的多任务处理比顺序执行两个任务要慢。但实际上,情况并非如此。如果一个 CPU 内核变得空闲或暂时不是很忙(例如,读取硬盘驱动器,或等待其他程序响应(Xcode 绘制 NSLog 输出)),那么它很可能会继续执行几秒钟GCD 项,因为它当前正在执行的一项卡住了。

    当然,大多数时候您拥有多个空闲 CPU 内核。

    它也不一定按照你给它的确切顺序执行事情。 GCD/内核可以控制这些细节。

    对于您的具体示例,Xcode 的调试器可能一次只能处理一个 NSLog() 事件(至少,它必须一次执行一个屏幕绘制)。您有两个队列,它们可能会同时开始执行。如果您同时发送两个NSLog() 语句,其中一个将等待另一个先完成。因为除了向 Xcode 打印东西之外什么都不做,这两个 GCD 队列将争先恐后地将日志数据发送到 Xcode。第一个有轻微的领先优势,但它非常轻微,通常不足以首先打开与 Xcode 的连接。

    这完全取决于在特定纳秒时间内硬件上可用的实际硬件资源。您无法预测,需要适当地构建队列以承担一些控制权。

    【讨论】:

      【解决方案2】:

      您从哪里了解到 GCD 不进行抢先式多任务处理?我认为你错了。它建立在系统提供的线程支持之上,因此分派到队列的 GCD 块可能会被抢先中断。

      您看到的行为正是我所期望的。第一个和第二个块被分派到同一个队列,因此 GCD 将确保第一个块在第二个块开始之前完成。但是,块 3 被分派到一个完全不同的队列(即将在单独的后台线程上运行),因此当线程由系统调度时,它的输出与其他两个块交错。

      【讨论】:

      • 这可能是语义问题。内核当然可以在执行块时抢占块(由于线程耗尽其 CPU 量或切换上下文),但 GCD 本身永远不会中断正在进行的块以执行不同的块。
      【解决方案3】:

      你读到的都是错的,除非你使用串行调度队列,否则所有块都会并发执行。

      【讨论】:

      • 他说的是 iOS,除了最新的硬件之外,所有的东西都只有一个 CPU 内核。 GCD 永远不会在单个内核上同时执行两个 CPU 密集型任务。据我了解,只有在系统上任何地方都没有任何 CPU 密集型任务处于活动状态时,它们才会并发。我认为他稍微误解了他在某处读到的东西。正如您所说,这并非完全错误。
      • 您对硬件的参与无关紧要,事实上,如果您调用 dispatch_async(dispatch_get_global_queue(0,0), ^{ 代码,您无法确定哪个块将首先执行或完成执行这里 });两次,即使在单个处理器上。您可以确定串行执行的唯一时间是当您使用串行调度队列时,您提交的每个块按提交顺序执行。从代码的角度来看,将硬件的细节带入讨论是无关紧要的,因为您不知道将在哪里执行。
      • 我看到在这种情况下@jgoldberg 完好无损地创建了两个串行调度队列,并将两个块提交给一个队列,一个提交给另一个,如果您查看提交给队列实际上是串行执行的,而提交给另一个队列的队列实际上是同时执行的。这是发帖人的一个误解:串行队列串行执行块,但每个串行队列将同时运行。
      • 我同意您在 cmets 中所说的一切。但我不同意你的回答,你说“所有块都将同时执行”。这不是真的。只有当有硬件资源可用时,它们才会同时执行。如果您安排 20,000 个 CPU 密集型块,它们将不会同时执行,因为内核中的多线程会对性能造成巨大影响,而 GCD 是专门为避免该性能问题而设计的。在 A4 CPU 上,它们可能一次执行一个。
      • 恐怕阿比错了。这不仅仅是一个偏向于硬件的决定,事实上,即使在 GCD 很乐意利用的单个 CPU 内核上也有机会实现并发。更多地将其视为一个线程池,无论硬件配置如何,其中任何一个都可能并发执行,也可能不并发执行,并且更关键的问题是“哪些被阻塞?” I/O 就是这样一种阻塞操作,如果给定的上下文切换回内核并需要阻塞直到其请求被处理,GCD 将根据需要(在合理范围内)创建更多线程。
      【解决方案4】:

      您的队列在 2 个并发的后台线程中工作。它们同时提供 NSLog 消息。当一个线程输出 NSlog 时,另一个线程等待。
      怎么了?

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-03-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-17
        相关资源
        最近更新 更多