Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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)
    }
like image 315
Ferox Avatar asked May 29 '19 21:05

Ferox


2 Answers

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.

like image 98
rmaddy Avatar answered Nov 17 '22 08:11

rmaddy


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
}

like image 2
matt Avatar answered Nov 17 '22 07:11

matt