根据我对 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 会使当前执行等待。我在苹果文档中找不到与此相关的任何内容。谁能解释一下这个吗?
GCD 不使用任何类型的“事件循环”。这是 Mac OS X 和 iOS 最新版本中的一项新内核功能,据我所知,实际上没有任何其他类似的技术。
目标是在硬件允许的情况下尽快完成执行您提供的所有代码。请注意,它的目标是最快完成时间,而不是最快开始时间。这是一个微妙的差异,但却是一个重要的差异,会对现实世界的工作方式产生影响。
如果只有一个空闲的CPU核心,那么理论上一次只会执行其中一个。因为单核内的多任务处理比顺序执行两个任务要慢。但事实上,情况并非如此。如果 CPU 核心暂时空闲或不是很忙(例如,读取硬盘,或等待其他程序响应(Xcode 绘制 NSLog 输出)),那么它很可能会继续执行某些程序GCD 项目,因为它当前正在做的项目被卡住了。
当然,大多数时候您将会拥有多个空闲 CPU 核心。
它也不一定会按照您给出的确切顺序执行操作。 GCD/内核可以控制这些细节。
对于您的具体示例,Xcode 的调试器可能一次只能处理一个
NSLog()
事件(至少,它必须一次绘制一个屏幕)。您有两个队列,它们可能会同时开始执行。如果您同时发送两个 NSLog()
语句,其中一个将等待另一个先完成。因为除了将内容打印到 Xcode 之外,您没有做任何事情,所以这两个 GCD 队列将争夺第一个将日志数据发送到 Xcode 的队列。第一个有轻微的领先优势,但这是一个非常轻微的领先,通常不足以首先打开与 Xcode 的连接。
这完全取决于在特定纳秒时间内硬件上可用的实际硬件资源。您无法预测它,并且需要适当地构建队列以承担一些控制。
您在哪里读到 GCD 不进行抢占式多任务处理?我认为你错了。它建立在系统提供的线程支持之上,因此分派到队列的 GCD 块可能会被抢先中断。
您所看到的行为正是我所期望的。第一个和第二个块被分派到同一个队列,因此 GCD 将确保第一个块在第二个块开始之前完成。然而,第三个块被分派到一个完全不同的队列(即将在单独的后台线程上运行),因此当线程由系统调度时,它的输出与其他两个块交错。
无论您读到的都是错误的,除非您使用串行调度队列,否则所有块都将同时执行。
您的队列在 2 个并发后台线程中工作。它们同时提供 NSLog 消息。当一个线程进行 NSlog 输出时,另一个线程等待。
怎么了?
扩展阿比的答案(+1):
所有“第一个块”将在“第二个块”开始之前完成,因为它们是提交到同一个串行队列的两个块;在第一个块在此串行队列上完成之前,第二个块不会启动;也许不用说,如果这个队列是并发队列,那么所有的赌注都消失了,但我们在这里讨论的是串行队列;
“第三块”位于单独的串行队列上,可以与分派到第一个队列的项目并行运行(特别是在多核 CPU 的世界中);和
尽管如此,
NSLog
本身是同步的;例如,您永远不会看到一个工作线程的一个 NSLog
输出在中线被另一个线程的另一个 NSLog
输出中断);作为历史参考,早在 Swift 的早期,print
语句是不同步的,所以你可能会看到多个线程散布的乱码输出,但他们已经解决了这个问题,Swift print
现在是同步的,也是。
我很抱歉回答一个老问题,但我想区分(a)两个单独的串行队列的并发/并行性;而且 (b) 各个
NSLog
语句的同步。