swift - 如何不 dispatch 如此频繁?

我已经阅读了很多 dispatch 的工作原理。但我对此还是有点困惑。

例如,如果我有

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

                 }

              }

          }


     }
}

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()

        }


    }


}

所以我们拥有的是 ViewController 操作。当我们开始行动时,我们分派(dispatch)到全局队列并发出 Api 请求。

1 在那一点(让它成为点 1)队列是否为队列收集了线程?

然后我们进行 Api.request 调用,它会再次调度到全局队列。

2 它是否排队到与点 1 相同的队列?或者它排队到另一个具有相同 QoS 的队列?还是CPU自己做决定? 它会创建新的 Thread 吗?其实我知道 GCD 决定自己创建线程,但我不明白

然后 Api 调用完成,在某些情况下我们分派(dispatch)到主队列

然后我们再次调度做“//在这里做其他事情”并且它是否创建新队列?还是新线程?

我还知道我们的 GCD 线程池限制为 64。这就是我害怕的原因。我也看到 wwdc 谈论线程爆炸但不明白所以如果我们经常从一个队列调度到另一个队列是否有线程爆炸的危险?

我知道为队列创建新线程的成本很高,而且我们不需要经常从一个队列调度到另一个队列,因为我们浪费时间进行调度。

但是我的示例是这样的错误调度吗?

最佳答案

基本上你对队列的理解是错误的,iOS默认为每个应用程序提供了几个调度队列(这些队列之间的唯一区别是它们保证的服务质量)和1个串行队列(显然是主队列)。

当然,您可以创建自己的串行和调度队列,但因为您的代码使用的是 dispatch.global,所以我将在这里只使用全局队列。

无论您的应用是否访问它们,它们始终可用。这就是为什么它们被称为全局队列 :D

引用苹果

The system provides each application with four concurrent dispatch queues. These queues are global to the application and are differentiated only by their priority level. Because they are global, you do not create them explicitly.

这些是所有应用程序都可用的非引用计数对象,因此您的第一个问题“调用将另一个调度发送到全局队列。”是不合逻辑的。当您访问 DispatchQueue.global(qos: 时不会创建队列,而是您只访问系统中已有的几个 Dispatch Queue 之一,并根据您选择的 QoS 添加您的任务。

让我们回答你的问题,

1 At that point (let it be point 1) does queue gathered thread for the queue ?

无法猜测Queue是否已经有线程,这些是全局队列,线程是由队列自己创建、处理和管理的。因此,如果 Queue 已经有一个计划任务要执行,或者如果它已经在执行一个任务,它可能有线程,否则可能没有。多少线程?同样,我们无法控制它,调度队列决定并发执行任务需要多少线程。

2 Does it queued to same queue as at point 1 ? or it queue to the another queue with the same QoS ? or CPU make decision by itself ? And does it create new Thread ? Actually I know that GCD make decision to create thread by itself but I don't make sense

您正在访问具有相同 QoS 的全局调度队列,userInitiated 所以显然您将任务添加到您在 Point1 中使用的同一队列。我希望现在您已经知道在访问 DispatchQueue.global(qos: 时您不会创建队列,而只是使用 iOS 提供的众多调度队列之一。

Actually I know that GCD make decision to create thread by itself but I don't make sense

老实说,你不必这样做,这就是逻辑抽象的全部意义所在,他们编写了一个名为 GCD api 的接口(interface)来隐藏低级 api 的复杂性,例如创建线程、管理和调度它

代码中的问题:

很明显

static func request(completion: @escaping (Result<String, NSError>) -> Void) {

        DispatchQueue.global(qos: .userInitiated).async {

.userInitiated 队列上调度 API 调用,因此是初始的

 DispatchQueue.global(qos: .userInitiated).async {

              Api.request { result in

没有意义。线程上下文切换代价高昂,应仅在有意义时才进行。虽然它不会在遇到第二个语句时再次切换,但无论如何初始切换完全没有用

DispatchQueue.global(qos: .userInitiated).async {

根据苹果文档

User-initiated tasks are second only to user-interactive tasks in their priority on the system. Assign this class to tasks that provide immediate results for something the user is doing, or that would prevent the user from using your app. For example, you might use this quality-of-service class to load the content of an email that you want to display to the user.

很明显,您使用 .userInitiated 队列来调度所有 api 调用,由于缺乏更好的词,我称之为滥用 userInitiated 调度队列。用户启动的任务在系统中的优先级仅次于用户交互任务。您真的希望所有长 API 调用都具有该优先级吗?如果你问我,那是你走的山路非常陡峭:)

我应该使用什么?

取决于您的需要,如果是简单的 API 调用,您可以使用 default 全局队列,如果您需要在后台配置上运行 API,您可以使用后台队列,显然不是 .userInitiated 完成所有洗衣工作。

希望对你有帮助

https://stackoverflow.com/questions/61900232/

相关文章:

javascript - onChange 在每次击键时触发

java - OPTIONS、TRACE 和 HEAD 请求方法的用例

asp.net-core - 脚手架MVC在继承IdentityDbContext时不加载Appli

amazon-web-services - CognitoIdentityCredentials 不

python - 我怎样才能看到 Pandas 的几个最大值?

javascript - 谷歌邮箱 : Button/Link which trigger a sc

python - matplotlib 为单个散点图添加具有多个条目的图例

scala - 为什么在使用 List 时得到的结果与使用 Tuple 时得到的结果不同?

python - 类型错误 : get_bind() got an unexpected keywo

python - 在对数刻度上生成均匀间隔的值(如 `np.linspace()` ,但在对数刻度上