使用 Dispatch Group 时异步函数需要很长时间

Async Functions taking long when using Dispatch Group

我正在使用 Firebase Storage 来存储我的应用程序用户的个人资料照片,其中最多可以有六张。因此,每当用户保存他们的图像时,我都会创建一个 DispatchGroup 并同时删除 Firebase 存储中所有以前关联的 photoUrls,并将新图像上传到 Firebase 存储。 Firebase 数据库中的用户对象也已更新。我想这是我实现方式的问题 DispatchGroup,所以我将提供我如何使用它的伪代码:

//PSEUDOCODE
func buttonPressed() {
    let group = DispatchGroup()
    for url in oldUrl {
        group.enter()
        asyncFuncThatDeletesImage(at: url) {
            group.leave()
        }
    }

    for image in newImages {
        group.enter()
        asyncFuncThatUploadsNewImage(image) {
            group.leave()
        }
    }

    group.notify(queue: .main) {
        //continue
    }
}

我的异步函数(asyncFuncThatDeletesImageasyncFuncThatUploadsNewImage 单独执行最多需要几秒钟。但是,当使用我的 DispatchGroup 时,.notify 直到大约 2 分钟后才被调用

这里是所有感兴趣的人的完整代码:

@IBAction func saveButtonPressed(_ sender: Any) {
    let oldUrls = parentVC.photoUrls
    activityIndicator.isHidden = false
    activityIndicator.startAnimating()
    let photoUploader = PhotoUploader()
    var data = [Data]()
    var urls: [Int:String] = [:]

    let group = DispatchGroup()
    for oldUrl in oldUrls {
        group.enter()
        let storageRef = Storage.storage().reference(forURL: oldUrl)

        //Removes image from storage
        storageRef.delete { error in
            if let error = error {
                print(error)
                group.leave()
            } else {
                // File deleted successfully
                print("File deleted, took \(Date().timeIntervalSince1970 - startTime) seconds")
                group.leave()
            }
        }
    }

    for (index,image) in imagesArray.enumerated() {
        group.enter()
        let imageData = image.jpegData(compressionQuality: 1)!
        data.append(imageData)
        photoUploader.upload(image: image, to: "Users/\(Auth.auth().currentUser!.uid)/photoUrls/\(index)", completion: {(url, error) in
            if error == nil {
                urls[index] = url
                group.leave()

                print("Image uploaded, took \(Date().timeIntervalSince1970 - startTime) seconds")
            } else {
                print(error?.localizedDescription)
                group.leave()
            }
        })
    }

    for i in imagesArray.count...6 {
        Database.database().reference().child("Users/\(Auth.auth().currentUser!.uid)/photoUrls/\(i)").removeValue()
    }

    group.notify(queue: .main) {
        //Do whatever I need to do
    }

这是我的函数 PhotoUploader class:

func upload(image: UIImage, to firebasePath: String, completion: @escaping (String?, Error?) -> Void) {
    //... creates data from the image...
    var data = NSData()
    // data = UIImageJPEGRepresentation(image, 0.8)! as NSData
    data = image.jpegData(compressionQuality: 0.8)! as! NSData
    //...sets the upload path
    let filePath = "\(Auth.auth().currentUser!.uid)\(String.randomStringWithLength(len: 20))" // path where you wanted to store img in storage
    let metaData = StorageMetadata()
    metaData.contentType = "image/jpg"

    let storageRef = Storage.storage().reference().child(filePath)
    storageRef.putData(data as Data, metadata: metaData){(metaData,error) in
        if let error = error {
            print(error.localizedDescription)
            completion(nil, error)
            return
        } else {
            storageRef.downloadURL(completion: { (url, error) in
                //Returns the url string to the newly uploaded image so that we can set the user's photoURL database property to this string
                Database.database().reference().child(firebasePath).setValue(url!.absoluteString)
                completion(url!.absoluteString, nil)
            })
        }
    }
}

可能是您将 notify 放入了 .main 队列。或者你的 async 函数不应该在 .main 队列中。无论哪种方式,我都会创建一个独立的 .concurrent 队列,并将您的 async 调用和 notify 放在上面。即

let customQueue = DispatchQueue(label: "customQueue", attributes: .concurrent)

...

func asyncFuncThatUploadsNewImage(image){
    customQueue.async{ ... }
}
func asyncFuncThatDeletesImage(image){
    customQueue.async{ ... }
}

...

group.notify(queue: customQueue) {
    //continue
}

在我看来你正确地使用了 DispatchGroup。

将事物放在主队列中不同于自定义队列有几个原因,其中之一是它是串行队列,而不是并发队列,而且(我认为)其他事物可能是 运行 在您不知道的主队列上,这使您必须做的推理变得复杂。

从 OP 编辑​​ 建议的编辑工作,但我不完全确定为什么。此外,在 group.notify(queue:) 方法中,即使我通过了自定义队列,我也不得不将包含的代码包装在主线程上。这很有趣,但这是有效的代码:

@IBAction func saveButtonPressed(_ sender: Any) {
    let oldUrls = parentVC.photoUrls
    activityIndicator.isHidden = false
    activityIndicator.startAnimating()
    let photoUploader = PhotoUploader()
    var data = [Data]()
    var urls: [Int:String] = [:]

    //Custom queue
    let customQueue = DispatchQueue(label: "customQueue", attributes: .concurrent)
    let group = DispatchGroup()
    for oldUrl in oldUrls {
        group.enter()
        customQueue.async {
            let storageRef = Storage.storage().reference(forURL: oldUrl)

            //Removes image from storage
            storageRef.delete { error in
                if let error = error {
                    print(error)
                    group.leave()
                } else {
                    // File deleted successfully
                    print("File deleted, took \(Date().timeIntervalSince1970 - startTime) seconds")
                    group.leave()
                }
            }
        }
    }

    for (index,image) in imagesArray.enumerated() {
        group.enter()
        customQueue.async {
            let imageData = image.jpegData(compressionQuality: 1)!
            data.append(imageData)
            photoUploader.upload(image: image, to: "Users/\(Auth.auth().currentUser!.uid)/photoUrls/\(index)", completion: {(url, error) in
                if error == nil {
                    urls[index] = url
                    group.leave()

                    print("Image uploaded, took \(Date().timeIntervalSince1970 - startTime) seconds")
                } else {
                    print(error?.localizedDescription)
                    group.leave()
                }
            })
        }
    }

    for i in imagesArray.count...6 {
        Database.database().reference().child("Users/\(Auth.auth().currentUser!.uid)/photoUrls/\(i)").removeValue()
    }

    group.notify(queue: customQueue) {
        DispatchQueue.main.async {
            //Do whatever I need to do
        }
    }