在任务完成之前调用的 Swift DispatchGroup 通知

Swift DispatchGroup notify called before task finish

提问人:LCSome 提问时间:10/9/2023 更新时间:10/9/2023 访问量:31

问:

我使用信号量和 DispatchGroup 处理并发网络。它在演示中工作正常。但是在我将代码复制到项目后,它不起作用。谁能找到问题?

演示:

 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)
    }

演示结果如下:

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

项目:

 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")
            }
            
        }
        
    }

以及项目结果:

finish
successs=2
successs=1
successs=0
successs=3
successs=5
successs=4
successs=7
successs=6
successs=8
successs=9

我找不到解决问题的方法,导致演示代码工作正常。有没有人能找到解决这个问题的理由?

Swift 并发 信号量 调度组

评论

0赞 HangarRash 10/9/2023
你想用信号量做什么?目前它没有做任何有用的事情。你从 10 点开始。减少它的调用。只有当信号量的值达到 0 时,它才会真正阻塞。由于递增了它,因此信号量不会在代码中执行任何有用操作。waitsignal
0赞 LCSome 10/9/2023
@HangarRash,感谢您的解释让我更加了解代码问题,一开始我想将 DispatchSemaphore(value: 1) 设置为逐 one.so 请求一个,这就是我使用信号量的原因。

答:

1赞 HangarRash 10/9/2023 #1

您有几个问题。您的演示实际上并没有模拟您的真实代码,因为您的演示实际上并不像调用 is 那样是异步的。如果你是异步的,那么你会发现你的演示会像你的真实代码一样失败。getRequestWithCallbackMCNetwork.share.requestgetRequestWithCallback

第一个问题是你的信号量没有做任何事情,所以你不妨删除所有信号量的使用。

第二个也是更重要的问题是,您不应该像在实际代码中那样用于调用异步代码。实际代码失败的原因是循环立即完成,并且所有调用在调用有机会运行之前很久就完成了。在演示中,由于不是异步的,因此对 的调用在返回之前(休眠后)不会完成。asyncasyncMCNetwork.share.requestgetRequestWithCallbackasyncgetRequestWithCallback

以下是重构真实代码的一种方法:

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")
        }
    }
}

这将同时对所有 10 个网络呼叫进行排队。调用并跟踪有多少个活动的异步网络调用。块内的代码将在所有 10 次网络调用完成(或错误)后调用。enterleavegroup.notify