我读过很多关于派遣工作的文章。但我对此仍然有点困惑。
例如,如果我有
class ViewController: UIViewController {
@IBAction func actionDoStuff(_ sender: UIButton) {
DispatchQueue.global(qos: .userInitiated).async {
Api.request { result in
//completes in main thread
//what if I need to dispatch again ?
DispatchQueue.global(qos: .userInitiated).async {
//Do other stuff here
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
和
class Api {
static func request(completion: @escaping (Result<String, NSError>) -> Void) {
DispatchQueue.global(qos: .userInitiated).async {
//url session configure
let url = URL(fileURLWithPath: "test.com")
URLSession.shared.dataTask(with: url) { data, response, error in
DispatchQueue.main.async {
completion(.success("Request are success")) //without error handler for simplifier
}
}.resume()
}
}
}
Run Code Online (Sandbox Code Playgroud)
所以我们拥有的是 ViewController 操作。当我们开始操作时,我们会分派到全局队列并发出 Api 请求。
1 此时(假设为点 1)队列是否为队列聚集了线程?
然后我们进行 Api.request 调用,这将再次调度到全局队列。
2 它是否在与点 1 相同的队列中排队?或者它排队到另一个具有相同 QoS 的队列?还是CPU自己决定?它会创建新的线程吗?其实我知道GCD自己决定创建线程,但我没有意义
然后 Api 调用完成,在某些情况下我们会分派到主队列
然后我们再次调度去做“//Do other stuff here”并且它会创建新队列吗?或新线程?
我还知道我们的 GCD 线程池限制为 64。这就是我担心的原因。我也看到过 wwdc 谈论线程爆炸,但不明白,所以如果我们经常从一个队列调度到另一个队列,是否有线程爆炸的危险?
我知道为队列创建新线程的成本很高,而且我们不需要经常从一个队列调度到另一个队列,因为我们浪费了调度时间。
但是我的例子是这样的错误调度吗?
基本上你对队列的理解是错误的,iOS默认为每个应用程序提供了几个调度队列(这些队列之间的唯一区别是它们保证的服务质量)和1个串行队列(这显然是主队列)。
当然,您可以创建自己的串行和调度队列,但因为您的代码使用的dispatch.global是我将在这里仅使用全局队列。
无论您的应用程序是否访问它们,它们始终可用。这就是为什么它们被称为全局队列:D
引用苹果的话
系统为每个应用程序提供四个并发调度队列。这些队列对于应用程序来说是全局的,仅通过优先级来区分。因为它们是全局的,所以您不必显式创建它们。
这些是可供所有应用程序使用的非引用计数对象,因此您的第一个问题“调用这将再次调度到全局队列。 ”是不合逻辑的。当您访问时,不会创建队列DispatchQueue.global(qos:,而是仅访问系统中已有的多个调度队列之一,并根据您选择的 QoS 将您的任务添加到其中。
让我们回答您的问题,
1 此时(假设为点 1)队列是否为队列聚集了线程?
无法猜测 Queue 是否已经有线程,这些是全局队列,线程是由队列本身创建、处理和管理的。因此,如果队列已经有一个计划任务要执行,或者它已经在执行一个任务,那么它可能有线程,否则可能没有。有多少线程?同样,我们无法控制它,调度队列决定它需要多少线程来并发执行任务。
2 它是否在与点 1 相同的队列中排队?或者它排队到另一个具有相同 QoS 的队列?还是CPU自己决定?它会创建新的线程吗?其实我知道GCD自己决定创建线程,但我没有意义
您正在以相同的 QoS 访问全局调度队列,userInitiated因此显然您已将任务添加到与 Point1 中使用的相同队列中。我希望现在您已经明白,访问时不需要创建队列,DispatchQueue.global(qos:而只需使用 iOS 提供的众多调度队列之一。
其实我知道GCD自己决定创建线程,但我没有意义
说实话,你不必这就是逻辑抽象的全部意义,他们编写了一个名为 GCD api 的接口来隐藏低级 api 的复杂性,例如创建线程、管理和调度线程
您的代码中的问题:
清楚地
static func request(completion: @escaping (Result<String, NSError>) -> Void) {
DispatchQueue.global(qos: .userInitiated).async {
Run Code Online (Sandbox Code Playgroud)
在队列上调度 API 调用.userInitiated,因此初始
DispatchQueue.global(qos: .userInitiated).async {
Api.request { result in
Run Code Online (Sandbox Code Playgroud)
没有意义。线程上下文切换的成本很高,只有在有意义时才应该进行。虽然遇到第二条语句时不会再次切换,但无论如何初始切换完全没用
DispatchQueue.global(qos: .userInitiated).async {
根据苹果文档
用户启动的任务在系统上的优先级仅次于用户交互任务。将此类分配给为用户正在执行的操作提供即时结果或阻止用户使用您的应用程序的任务。例如,您可以使用此服务质量类来加载要向用户显示的电子邮件内容。
显然,您使用 .userInitiated 队列来调度所有 api 调用,因为缺乏更好的词语,我称之为滥用userInitiated调度队列。用户启动的任务在系统上的优先级仅次于用户交互任务。您真的希望所有长 API 调用都具有该优先级吗?如果你问我的话,你所走的山路非常陡峭:)
我应该用什么?
根据您的需要,如果是简单的 API 调用,您可能会使用default全局队列,如果您需要在后台配置上运行 API,您可能会使用后台队列,显然不是.userInitiated为了完成所有的洗衣工作。
希望能帮助到你
| 归档时间: |
|
| 查看次数: |
310 次 |
| 最近记录: |