Ferox
Ferox

Reputation: 93

Dispatch Group wait stuck forever

I have a function making multiple HTTP requests with Alamofire. I want to wait for all of them to finish in order to return a value. However, it gets stuck at dispatch.wait()

class func getActionField(fieldid: String, completion: @escaping (_ res: [String: [Double]]) -> Void) {
        var resreturn: [String: [Double]] = ["temperature":[], "humidity":[], "ph":[], "light":[]]
        let dispatch = DispatchGroup()
        dispatch.enter()
        Alamofire.request(url + "aktionsdaten/temperatur/" + fieldid, method: .get).responseJSON{ response in
            resreturn["temperature"] = response.result.value as! NSArray as? [Double] ?? [0.0,0.0]
            dispatch.leave()
        }
        dispatch.enter()
        Alamofire.request(url + "aktionsdaten/light/" + fieldid, method: .get).responseJSON{ response in
            resreturn["light"] = response.result.value as! NSArray as? [Double] ?? [0.0,0.0]
            dispatch.leave()
        }
        dispatch.enter()
        Alamofire.request(url + "aktionsdaten/ph/" + fieldid, method: .get).responseJSON{ response in
            resreturn["ph"] = response.result.value as! NSArray as? [Double] ?? [0.0,0.0]
            dispatch.leave()
        }
        dispatch.enter()
        Alamofire.request(url + "aktionsdaten/feuchtigkeit/" + fieldid, method: .get).responseJSON{ response in
            resreturn["humidity"] = response.result.value as! NSArray as? [Double] ?? [0.0,0.0]
            dispatch.leave()
        }
        dispatch.wait()
        completion(resreturn)
    }

Upvotes: 7

Views: 2903

Answers (2)

rmaddy
rmaddy

Reputation: 318794

Assuming that getActionField is being called on the main queue and the understanding that Alamofire calls its completion blocks on the main queue (bad design in my opinion), you are running into a deadlock since the call to wait is now blocking the main queue and none of the calls to leave can be made.

You must never use the same thread to call wait and leave.

The simplest solution is to replace the use of wait with notify.

group.notify(queue: DispatchQueue.main) {
    completion(resreturn)
}

You should avoid the use of wait in general. Especially if you are already using a completion handler and there is no need for the method to wait.

Upvotes: 6

matt
matt

Reputation: 534958

You have this all set up correctly, using a completion handler after all your asynchronous tasks are finished. The only problem is that you're using the DispatchGroup pattern incorrectly. Here's the correct pattern (this is pseudocode):

let group = DispatchGroup()
group.enter()
queue1.async {
    // ... do task here ...
    group.leave()
}
group.enter()
queue2.async {
    // ... do task here ...
    group.leave()
}
group.enter()
queue3.async {
    // ... do task here ...
    group.leave()
}
// ... more as needed ...
group.notify(queue: DispatchQueue.main) {
    // finished! call completion handler or whatever
}

Upvotes: 3

Related Questions