I use semaphore and DispatchGroup deal with the concurrency network.It's working fine in the demo.But it's not working after I copy the code to the project.Can anyone find the problem?
demo:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
let queue = DispatchQueue.global(qos: .default)
let semaphore = DispatchSemaphore(value: 10)
let group = DispatchGroup()
var stop = false
queue.async {
for i in 0..<10{
semaphore.wait()
queue.async(group: group, execute: { [self] in
if(stop){
return;
}
print("beigin = \(i)")
getRequestWithCallback { Bool in
print("end = \(i)")
semaphore.signal()
} error: { Bool in
print("end = \(i)")
stop = true
semaphore.signal()
}
})
}
group.notify(queue: queue) {
print("finish")
}
}
}
func getRequestWithCallback(success:successCallBack, error:errorCallBack){
sleep(2)
success(true)
}
and the demo result:
beigin = 8
beigin = 3
beigin = 7
beigin = 0
beigin = 4
beigin = 2
beigin = 9
beigin = 5
beigin = 6
beigin = 1
end = 8
end = 2
end = 5
end = 7
end = 3
end = 0
end = 4
end = 1
end = 6
end = 9
finish
project:
func uploadEventTrackingFiles() {
let array_all = UserDefaults.standard.array(forKey: "eventArray") ?? Array()
let params = array_all.prefix(10)
var para = [String : Any]()
let url = "https://dc-o.api.leiniao.com/frontreport/custom"
MCNetwork.share.headers = ["Content-Type": "application/json"]
let queue = DispatchQueue.global(qos: .default)
let semaphore = DispatchSemaphore(value: 10)
let group = DispatchGroup()
var stop = false
queue.async {
for i in 0..<params.count{
semaphore.wait()
queue.async(group: group, execute: {
if(stop){
return
}
para = params[i] as! [String : Any]
MCNetwork.share.request(url: url, Method: .get,Para: para,encoding:URLEncoding.default) { obj in
print("successs=\(i)")
semaphore.signal()
}RError: { err in
stop = true
print("error=\(i)")
semaphore.signal()
}
})
}
group.notify(queue: queue) {
var array_all = UserDefaults.standard.array(forKey: "eventArray") ?? Array()
array_all.removeSubrange(0..<10)
UserDefaults.standard.setValue(array_all, forKey: "eventArray")
print("finish")
}
}
}
and the project result:
finish
successs=2
successs=1
successs=0
successs=3
successs=5
successs=4
successs=7
successs=6
successs=8
successs=9
I can't find the way deal with the problem cause the demo code working fine. Does anybody can find the reason to work out this?
You have several issues. Your demo doesn't actually simulate your real code because your demo getRequestWithCallback
isn't actually asynchronous like the call to MCNetwork.share.request
is. If you made getRequestWithCallback
to be asynchronous then you would find that your demo would fail like your real code.
The first issue is that your semaphore isn't doing anything so you may as well remove all use of the semaphore.
The second, and more important, issue is that you should not use async
to call asynchronous code like you do in your real code. The reason the real code fails is because the loop completes immediately and all calls to async
complete long before the calls to MCNetwork.share.request
have a chance to run. In the demo, since getRequestWithCallback
isn't asynchronous, the calls to async
don't complete until getRequestWithCallback
returns (after the sleep).
Here's one way to refactor your real code:
func uploadEventTrackingFiles() {
let array_all = UserDefaults.standard.array(forKey: "eventArray") ?? Array()
let params = array_all.prefix(10)
var para = [String : Any]()
let url = "https://dc-o.api.leiniao.com/frontreport/custom"
MCNetwork.share.headers = ["Content-Type": "application/json"]
let queue = DispatchQueue.global(qos: .default)
let group = DispatchGroup()
queue.async {
for i in 0..<params.count {
para = params[i] as! [String : Any]
group.enter()
MCNetwork.share.request(url: url, Method: .get,Para: para,encoding:URLEncoding.default) { obj in
print("success=\(i)")
group.leave()
} RError: { err in
print("error=\(i)")
group.leave()
}
}
group.notify(queue: queue) {
var array_all = UserDefaults.standard.array(forKey: "eventArray") ?? Array()
array_all.removeSubrange(0..<10)
UserDefaults.standard.setValue(array_all, forKey: "eventArray")
print("finish")
}
}
}
This will queue up all 10 network calls concurrently. The calls to enter
and leave
keep track of how many active async network calls there are. The code inside the group.notify
block will be called after all 10 network calls complete (or error).