Grand Central Dispatch Multiple DispatchGroups

自闭症网瘾萝莉.ら 提交于 2019-12-25 07:40:00

问题


I have three async calls. One returns data, and two return images from S3 using firebase. I have one background DispatchQueue, and three dispatch groups. I need a way for them to execute synchronously, but they don't! I have tried everything, and .notify executes immediately, which is wrong.

The output of this is:
images done
all is done
group.notify is done
getImages() is done

What I want to understand is why is imagesdone executed before group.notify is done? I need to execute the first group, then imagesGroup, then avaGroup.

I essentially have three async calls, and the 2nd/3rd may be multiple async calls. How can I wait for them to complete, then execute subsequent calls?

func loadFriendPhotos() {

    let backgroundQueue = DispatchQueue(label: "com.app.queue",
                                        qos: .utility,
                                        target: nil)

    let group = DispatchGroup()
    let imageGroup = DispatchGroup()
    let avaGroup = DispatchGroup()
    typealias tempAlias = (username:String, imageURL: String, pathUrl:String)

    var tempAliasArray = [tempAlias]()
    var imageArray = [UIImage]()
    var avaImageArray = [UIImage]()

    group.enter()
    let workItem = DispatchWorkItem {
        databaseRef.child("friendPhotos").child(globalUsername).observeSingleEvent(of: .value, with: { (snapshot) in
            if snapshot.exists() {

                let enumerator = snapshot.children
                var childrenCount = snapshot.childrenCount


                while let rest = enumerator.nextObject() as? FIRDataSnapshot {

                    let name = rest.childSnapshot(forPath: "username").value as! String
                    let downloadURL = rest.childSnapshot(forPath: "downloadURL").value as! String
                    let uid = rest.childSnapshot(forPath: "uid").value as! String
                    let pathURL = rest.childSnapshot(forPath: "pathURL").value as! String
                    let downloadURLRef = storage.reference(forURL: downloadURL)

                    let newTempAlias = tempAlias(name, downloadURL, pathURL)
                    tempAliasArray.append(newTempAlias)
                }
                group.leave()
            }
        })
    }

    func getAvaImages() {
        for index in tempAliasArray{
            avaGroup.enter()
            let avaItem = DispatchWorkItem {
                let avaURLRef = storage.reference(forURL: index.1)
                avaURLRef.data(withMaxSize: 2 * 1024 * 1024) { (data,error) in
                    if (error == nil) {
                        print("success!")
                        let picData = UIImage(data:data!)
                        avaImageArray.append(picData!)

                    } else {
                        print(error?.localizedDescription)
                    }
                    print("we left getAvaImages()")
                    avaGroup.leave()

                }
            }
            backgroundQueue.async(execute: avaItem)
        }
    }

    func getImages() {
        for index in tempAliasArray{
            imageGroup.enter()
            let imageItem = DispatchWorkItem {
                let downloadURLRef = storage.reference(forURL: index.1)
                downloadURLRef.data(withMaxSize: 2 * 1024 * 1024) { (data,error) in
                    if (error == nil) {
                        let picData = UIImage(data:data!)
                        imageArray.append(picData!)
                    } else {
                        print(error?.localizedDescription)
                    }
                    print("we left getImages()")
                    imageGroup.leave()
                }
            }
            backgroundQueue.async(execute: imageItem)
        }
    }

    backgroundQueue.sync(execute: workItem)

    group.notify(queue: DispatchQueue.main, execute: {
        print("group.notify is done")
        getImages()
    })
    imageGroup.notify(queue: DispatchQueue.main, execute: {
        print("images done")
        getAvaImages()
    })
    avaGroup.notify(queue: DispatchQueue.main, execute: {
        print("all is done")
    })
 }

回答1:


You're calling imageGroup.notify at a point that there is nothing in imageGroup, so it immediately notifies. Similarly, avaGroup notifies immediately since it is empty at the point that you call it.

But I don't understand all the indirection and groups and three arrays. Why do you have to wait for all the images to download before you deal with each image? Why not just kick off the next step inside the original loop for just one element? Something like this:

var allTheResults = []

group.enter() // Enter once for the top-level
kickOffAsyncWithCompletion { resultList in

    for item in resultList {
        let resultThing = doSomeStuffToSetupNextLevel()

        group.enter() // Enter once per thing we iterate over

        kickOffNestedAsync(with: resultThing, completion: { result in
            let finalResult = computeFinalResult(fromResult: result)
            allTheResults.append(finalResult)
            group.leave() // leave once per thing we iterate over
        })
    }
    group.leave() // leave once for the top level
}

group.notify { doThing(withFinalResults: allTheResults) }

If you want to coordinate the endpoint of set of operations, you want all those operations to be working with the same group, not separate groups per operation.



来源:https://stackoverflow.com/questions/41493311/grand-central-dispatch-multiple-dispatchgroups

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!