繁体   English   中英

使用 DispatchGroup 时异步调用阻塞主线程

[英]Async call blocking main thread when using DispatchGroup

我正在尝试从 FireStore 数据库中获取文档。 在我的 function 中前进之前,我需要加载这些文档。 这是供参考的代码:

调用 FireStore 服务函数的视图 controller:

    let service = FirestoreServices()
    service.get(cottage: "test123456789") { model in
        
        nextViewController.cottageModel = model!
        self.present(nextViewController, animated:true, completion:nil)
        
    }

正在调用的 FireStore 服务方法:

func get(cottage: String, completionHandler: @escaping (CottageTrip?) -> ()) {
    
    //get a reference to the firestore
    let db = Firestore.firestore()
    
    //get the references to the collections
    let cottageRef = db.collection("cottages").document(cottage)
    
    //get the fields from the initial document and load them into the cottage model
    cottageRef.getDocument { (document, error) in
        if let document = document, document.exists {
            
            //create the cottage model to return
            let cottageModel: CottageTrip = CottageTrip()
            
            //get the subcollections of this document
            let attendeesCollection = cottageRef.collection("attendees")
            //other collections
            
            //here I get all info from initial document and store it in the model
            
            let group = DispatchGroup()
            
            print("here")
            
            group.enter()
            
            //get the attendees
            DispatchQueue.global(qos: .userInitiated).async {
                
                attendeesCollection.getDocuments() { (querySnapshot, err) in
                    print("here2")
                    if let err = err {
                            print("Error getting documents: \(err)")
                    } else {
                        //get data
                    }
                    group.leave()
                }
                
            }
            
            print("after async call")
            
            //wait here until the attendees list is built
            group.wait()
            
            print("after wait")
            
            //create the cars
            carsCollection.getDocuments() { (querySnapshot, err) in
                print("in car collection get doc call")
                if let err = err {
                        print("Error getting documents: \(err)")
                } else {
                    //get car data
                    }
                }
            }
            
            //this is where she should block until all previous get document operations complete
            
            completionHandler(cottageModel)
            
        } else {
            print("Document does not exist")
            completionHandler(nil)
        }
        
    }
    
}

我意识到print("here2")永远不会打印,所以它似乎阻塞在group.wait()上。 我需要使用group.wait()而不是 notify,因为我需要此 function 仅在加载参加者集合后才能访问子集合和文档,因为我需要这些值用于子集合和文档。 我在网上阅读了很多答案,大多数人在这种情况下使用group.wait()但由于某种原因,如果不锁定和冻结应用程序,我无法让它为我工作。

正如 algrid 指出的那样,您遇到了死锁,因为您正在等待主线程,Firestore 需要调用它的闭包。

作为一般规则,避免调用wait ,你不会死锁。 使用notify ,只需在该notify闭包内调用您的闭包。

因此,例如,假设您不需要attendees的结果来查询cars ,您可以只使用notify调度组模式,例如

func get(cottage: String, completionHandler: @escaping (CottageTrip?) -> Void) {
    let db = Firestore.firestore()
    let cottageRef = db.collection("cottages").document(cottage)

    cottageRef.getDocument { document, error in
        guard let document = document, document.exists else {
            print("Document does not exist")
            completionHandler(nil)
            return
        }

        let cottageModel = CottageTrip()

        let attendeesCollection = cottageRef.collection("attendees")
        let carsCollection = cottageRef.collection("cars")

        let group = DispatchGroup()

        group.enter()
        attendeesCollection.getDocuments() { querySnapshot, err in
            defer { group.leave() }

            ...
        }

        group.enter()
        carsCollection.getDocuments() { querySnapshot, err in
            defer { group.leave() }

            ...
        }

        group.notify(queue: .main) {
            completionHandler(cottageModel)
        }
    }
}

另外,顺便说一句,您不必将任何内容分派到全局队列,因为这些方法已经是异步的。


如果您需要一个结果来启动下一个,您可以嵌套它们。 这会更慢(因为你放大了网络延迟效应),但也完全消除了对组的需要:

func get(cottage: String, completionHandler: @escaping (CottageTrip?) -> Void) {
    let db = Firestore.firestore()
    let cottageRef = db.collection("cottages").document(cottage)

    cottageRef.getDocument { document, error in
        guard let document = document, document.exists else {
            print("Document does not exist")
            completionHandler(nil)
            return
        }

        let cottageModel = CottageTrip()

        let attendeesCollection = cottageRef.collection("attendees")
        let carsCollection = cottageRef.collection("cars")

        attendeesCollection.getDocuments() { querySnapshot, err in
            ...

            carsCollection.getDocuments() { querySnapshot, err in
                ...

                completionHandler(cottageModel)

            }
        }
    }
}

无论哪种方式,我可能倾向于将其分解为单独的功能,因为它有点毛茸茸,但想法是一样的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM