休眠的NSThread与之后的GCD调度相比



也许这个问题以前以不同的形式被问过。但我想我对这个问题的看法不同。我正在为整个代码库做一些优化任务,并进行大量重构,这也将提高代码的可读性。

所以我在某个地方找到了[NSThread sleepForTimeInterval:]方法,对于延迟,我总是使用GCD的dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(<#delayInSeconds#> * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{})方法。所以问题是哪个更好?

这是一个巨大的差异。

[NSThread sleepForTimeInterval:]

阻止当前线程。如果当前线程是主线程,那就太可怕了。根据你正在做的事情,它在其他线程上可能很好,也可能很糟糕。

dispatch_after

不会阻塞当前线程。当当前线程继续以其愉快的方式运行时,块将排队等候在指定队列上的未来时间运行。

如果没有任何具体的上下文,我会说dispatch_after在几乎所有情况下都是更好的方法。

在考虑用GCD的dispatch_after替换[NSThread sleepForTimeInterval:]时,需要考虑以下几点。正如@rmaddy所说,[NSThread sleepForTimeInterval:]会阻塞调用它的线程。正如所指出的,这对主线程,以及更常见的任何有运行循环的线程来说都是非常糟糕的(因为运行循环在调用期间不会迭代)

如果你有一个带有运行循环的线程,你可以"旋转"运行循环一段时间,这允许其他运行循环源保持响应,而执行的调用线程仍然有效地被阻止。旋转运行循环的细节超出了问题的范围,但使用运行循环是解决这个问题的另一种方法(尽管如果你没有被其他依赖运行循环的API(例如NSStream)强制使用,那么GCD可能仍然是首选。)

避免使用[NSThread sleepForTimeInterval:]阻塞线程的主要高级原因是线程是有限的资源。如果您要创建一个循环来生成许多线程,所有这些线程随后都因调用[NSThread sleepForTimeInterval:]而被阻塞,那么您最终将无法创建更多的线程。线程耗尽对于框架中特别优雅的解决方案来说并不是一个问题,因为通常情况下,框架希望您在架构级别通过使用更好的抽象(如GCD或运行循环)来避免这种情况。

CCD_ 10与CCD_。如果您的代码执行了类似[[NSThread currentThread] threadDictionary][@"foo"] = @"bar";的操作,那么在[NSThread sleepForTimeInterval:]返回后,您将处于完全相同的线程上,因此您放入线程本地存储的值仍将存在。使用dispatch_after,无法保证您的块将在其入队的同一线程上运行。(在有其他后台任务的过程中,这种偶然发生的总体可能性也相当低。)总的来说,将TLS与GCD一起使用不是一个好主意。这通常是可以的,因为在块闭包中捕获变量的能力可以解决人们历史上使用TLS解决的许多问题。

线程本地存储是解决给定问题时非常有用的东西之一,但在这样的重构面前可能会使代码变得脆弱。更糟糕的是,您可能无法知道代码的其他部分依赖TLS,除非看到事情失败(或行为怪异)。

长话短说,这里没有足够的信息来说明哪种机制最适合你的情况。不过,如果从零开始,我很难想出选择使用[NSThread sleepForTimeInterval:]而不是使用更现代的延迟机制来阻止线程的原因。

最新更新