GCD的dispatch_async是否会等待NSLog()?

问题描述 投票:0回答:5

根据我对 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 会使当前执行等待。我在苹果文档中找不到与此相关的任何内容。谁能解释一下这个吗?

ios cocoa concurrency grand-central-dispatch nslog
5个回答
9
投票

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

目标是在硬件允许的情况下尽快完成执行您提供的所有代码。请注意,它的目标是最快完成时间,而不是最快开始时间。这是一个微妙的差异,但却是一个重要的差异,会对现实世界的工作方式产生影响。

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

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

它也不一定会按照您给出的确切顺序执行操作。 GCD/内核可以控制这些细节。

对于您的具体示例,Xcode 的调试器可能一次只能处理一个

NSLog()
事件(至少,它必须一次绘制一个屏幕)。您有两个队列,它们可能会同时开始执行。如果您同时发送两个
NSLog()
语句,其中一个将等待另一个先完成。因为除了将内容打印到 Xcode 之外,您没有做任何事情,所以这两个 GCD 队列将争夺第一个将日志数据发送到 Xcode 的队列。第一个有轻微的领先优势,但这是一个非常轻微的领先,通常不足以首先打开与 Xcode 的连接。

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


2
投票

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

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


2
投票

无论您读到的都是错误的,除非您使用串行调度队列,否则所有块都将同时执行。


0
投票

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


0
投票

扩展阿比的答案(+1):

  • 所有“第一个块”将在“第二个块”开始之前完成,因为它们是提交到同一个串行队列的两个块;在第一个块在此串行队列上完成之前,第二个块不会启动;也许不用说,如果这个队列是并发队列,那么所有的赌注都消失了,但我们在这里讨论的是串行队列;

  • “第三块”位于单独的串行队列上,可以与分派到第一个队列的项目并行运行(特别是在多核 CPU 的世界中);和

  • 尽管如此,

    NSLog
    本身是同步的;例如,您永远不会看到一个工作线程的一个
    NSLog
    输出在中线被另一个线程的另一个
    NSLog
    输出中断);作为历史参考,早在 Swift 的早期,
    print
    语句是不同步的,所以你可能会看到多个线程散布的乱码输出,但他们已经解决了这个问题,Swift
    print
    现在是同步的,也是。

我很抱歉回答一个老问题,但我想区分(a)两个单独的串行队列的并发/并行性;而且 (b) 各个

NSLog
语句的同步。

© www.soinside.com 2019 - 2024. All rights reserved.