睡眠 NSThread 与。 GCD 调度后
Sleeping NSThread Vs. GCD dispatch after
也许这个问题之前已经以不同的形式被问过。但我认为我对此有不同的看法。我正在为整个代码库做一些优化任务和大量的重构,这也会提高代码的可读性。
所以我发现 [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
在几乎所有情况下都是更好的方法。
在考虑将 [NSThread sleepForTimeInterval:]
的用途替换为 GCD 的 dispatch_after
时,有几件事需要考虑。正如@rmaddy 所说,[NSThread sleepForTimeInterval:]
阻塞了调用它的线程。正如所指出的,这对主线程非常不利,更普遍的是任何带有 运行 循环的线程(因为 运行 循环不会在该调用期间迭代。)
如果你有一个带有 运行 循环的线程,你可以 "spin" 运行 循环一段时间,这允许其他 运行 循环源保持响应,而执行的调用线程保持有效阻塞。旋转 运行 循环的详细信息超出了问题的范围,但是使用 运行 循环是解决此问题的另一种方法(尽管如果您不被迫使用 GCD 可能仍然是首选运行 由其他依赖于它们的 API 循环,例如 NSStream
。)
避免使用 [NSThread sleepForTimeInterval:]
阻塞线程的主要高级原因是线程是有限的资源。如果您要创建一个产生许多线程的循环,所有这些线程随后都因调用 [NSThread sleepForTimeInterval:]
而被阻塞,您最终将无法创建更多线程。线程耗尽不是框架中特别优雅的解决方案的问题,因为通常情况下,框架希望您在体系结构级别通过使用更好的抽象(如 GCD 或 运行 循环)来避免这种情况。
[NSThread sleepForTimeInterval:]
与 dispatch_after
有很大不同的另一种情况是在使用线程本地存储时。如果您的代码执行了类似 [[NSThread currentThread] threadDictionary][@"foo"] = @"bar";
的操作,那么在 [NSThread sleepForTimeInterval:]
returns 之后,您将处于完全相同的线程中,因此您放入线程本地存储的值仍将存在.使用 dispatch_after
不能保证你的块将 运行 在它被排队的同一个线程上。 (并且在有其他后台任务在运行的进程中,偶然发生这种情况的总体可能性也相当低。)一般来说,将 TLS 与 GCD 一起使用并不是一个好主意。这通常没问题,因为在块闭包中捕获变量的能力可以解决人们过去使用 TLS 解决的许多问题。
线程本地存储是解决给定问题时非常有用的东西之一,但在面对这样的重构时会使代码变得脆弱。更糟糕的是,您可能无法知道代码的其他部分依赖于 TLS,除非看到事情失败(或行为怪异)。
长话短说,这里没有足够的信息说明哪种机制最适合您的情况。如果从头开始,我很难想出一个人会选择使用 [NSThread sleepForTimeInterval:]
而不是使用更现代的延迟机制来阻塞线程的原因。
也许这个问题之前已经以不同的形式被问过。但我认为我对此有不同的看法。我正在为整个代码库做一些优化任务和大量的重构,这也会提高代码的可读性。
所以我发现 [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
在几乎所有情况下都是更好的方法。
在考虑将 [NSThread sleepForTimeInterval:]
的用途替换为 GCD 的 dispatch_after
时,有几件事需要考虑。正如@rmaddy 所说,[NSThread sleepForTimeInterval:]
阻塞了调用它的线程。正如所指出的,这对主线程非常不利,更普遍的是任何带有 运行 循环的线程(因为 运行 循环不会在该调用期间迭代。)
如果你有一个带有 运行 循环的线程,你可以 "spin" 运行 循环一段时间,这允许其他 运行 循环源保持响应,而执行的调用线程保持有效阻塞。旋转 运行 循环的详细信息超出了问题的范围,但是使用 运行 循环是解决此问题的另一种方法(尽管如果您不被迫使用 GCD 可能仍然是首选运行 由其他依赖于它们的 API 循环,例如 NSStream
。)
避免使用 [NSThread sleepForTimeInterval:]
阻塞线程的主要高级原因是线程是有限的资源。如果您要创建一个产生许多线程的循环,所有这些线程随后都因调用 [NSThread sleepForTimeInterval:]
而被阻塞,您最终将无法创建更多线程。线程耗尽不是框架中特别优雅的解决方案的问题,因为通常情况下,框架希望您在体系结构级别通过使用更好的抽象(如 GCD 或 运行 循环)来避免这种情况。
[NSThread sleepForTimeInterval:]
与 dispatch_after
有很大不同的另一种情况是在使用线程本地存储时。如果您的代码执行了类似 [[NSThread currentThread] threadDictionary][@"foo"] = @"bar";
的操作,那么在 [NSThread sleepForTimeInterval:]
returns 之后,您将处于完全相同的线程中,因此您放入线程本地存储的值仍将存在.使用 dispatch_after
不能保证你的块将 运行 在它被排队的同一个线程上。 (并且在有其他后台任务在运行的进程中,偶然发生这种情况的总体可能性也相当低。)一般来说,将 TLS 与 GCD 一起使用并不是一个好主意。这通常没问题,因为在块闭包中捕获变量的能力可以解决人们过去使用 TLS 解决的许多问题。
线程本地存储是解决给定问题时非常有用的东西之一,但在面对这样的重构时会使代码变得脆弱。更糟糕的是,您可能无法知道代码的其他部分依赖于 TLS,除非看到事情失败(或行为怪异)。
长话短说,这里没有足够的信息说明哪种机制最适合您的情况。如果从头开始,我很难想出一个人会选择使用 [NSThread sleepForTimeInterval:]
而不是使用更现代的延迟机制来阻塞线程的原因。