从我所读到的关于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文档中找不到任何与此相关的内容。有人能解释一下吗?
5条答案
按热度按时间643ylb081#
GCD不使用任何类型的“事件循环”。它是MacOSX和iOS最新版本中的一个新内核特性,据我所知,它没有任何其他类似的技术。
目标是在硬件允许的范围内尽快完成执行您给予的所有代码。请注意,它的目标是最快的完成时间,而不是最快的开始时间。这是一个细微的区别,但却是一个重要的区别,它会对真实的世界产生影响。
如果你只有一个空闲的CPU核心,那么理论上一次只会执行其中一个。因为单个核心内的多任务比顺序执行两个任务要慢。但实际上,情况并非如此。如果一个CPU核心暂时空闲或不很忙碌,(例如,阅读硬盘驱动器,或等待其他程序响应(Xcode绘制NSLog输出)),那么它很可能会执行第二个GCD项,因为它现在做的这个被卡住了
当然,大多数时候你会有不止一个空闲的CPU核心。
它也不一定会按照你给予的顺序执行。GCD/内核可以控制这些细节。
对于您的特定示例,Xcode的调试器可能一次只能处理一个
NSLog()
事件(至少,它必须一次绘制一个屏幕).你有两个队列,它们可能会同时开始执行。如果你同时发送两个NSLog()
语句,其中一个会等待另一个先完成。因为你什么都没做,当你把东西打印到Xcode上时,这两个GCD队列会争先恐后地把日志数据发送到Xcode上。第一个队列稍微领先一点,但这是一个非常轻微的,通常不足以让它首先打开与Xcode的连接。这完全取决于在特定的纳秒时间内硬件上可用的实际硬件资源。您无法预测它,并且需要适当地构建队列以进行一些控制。
2ledvvac2#
你是从哪里读到GCD不做抢占式多任务处理的?我想你错了。它是建立在系统提供的线程支持之上的,所以分配到队列的GCD块可能会被抢占式中断。
你所看到的行为正是我所期望的。第一个和第二个块被分派到同一个队列,所以GCD会确保第一个块在第二个块开始之前完成。然而,块3被分派到一个完全不同的队列(即将在一个单独的后台线程上运行),所以它的输出与其他两个块交错,因为线程是由系统调度的。
2j4z5cfb3#
无论你读到什么都是错误的,除非你使用串行调度队列,否则所有的块都将并发执行。
wr98u20j4#
你的队列在两个并发的后台线程中工作。它们并发地提供NSLog消息。当一个线程输出NSlog时,另一个线程等待。
你怎么了?
7nbnzgx95#
扩展Ahbi的answer(+1):
NSLog
本身是同步的;例如,您永远不会看到来自一个工作线程的一个NSLog
的输出被来自另一个线程的另一个NSLog
的输出中断);作为历史参考,在Swift早期,print
语句没有同步,所以你可能会看到来自多个线程的乱码输出,但他们已经解决了这个问题,Swiftprint
现在也同步了。很抱歉打断了一个老问题,但我想区分(a)两个单独的串行队列的并发/并行性;以及(b)单个
NSLog
语句的同步。在一个不太相关的问题上,有评论指出,一个GCD队列上的一个工作项不能抢占另一个队列上的另一个项。(这对OP的问题来说并不突出,但在随后的回答/评论中是一个混淆的来源。)
因此,我在一个6核设备上进行了一个实验,并行运行8个相同的计算密集型计算,在前四个任务之后0.5秒在单独的队列上启动后四个任务(但前四个任务仍在进行中)。
如果我使用Instruments的“Point of Interest”工具,我可以看到这两个队列是否具有相同的优先级(在我的示例中为“默认”QoS),没有表现出抢占。它一次最多运行六个,最后两个等待在其他工作线程之一上完成工作时释放一个核心:
x1c 0d1x的数据
但是,如果我将第一个队列降低到“实用程序”QoS,并将第二个队列提高到“用户启动”QoS,我会看到稍后提交的高优先级任务抢占了较早启动的低优先级任务:
的
并且,将我的检查范围设置为与高优先级任务相关联的时间间隔,我可以看到低优先级任务在享受充分的CPU利用率时,在高优先级任务运行时被丢弃:
的
底线是,在资源争用的情况下,高优先级的GCD任务 * 可以 * 抢占低优先级的任务。顺便说一句,Swift并发(
async
-await
)没有表现出这种行为,但GCD(和手动NSThread
代码)可以。