【问题标题】:Waiting until a certain task completely finish to execute another task等到某个任务完全完成再执行另一个任务
【发布时间】:2019-05-03 18:29:52
【问题描述】:

我正在尝试从 Firebase 实时数据库中检索一些加密消息,对其进行解密,然后将它们显示在 CollectionView 中。解密过程是成功的,但是我遇到了一个关于多线程的问题:添加到Messages数组中的获取解密消息的顺序是错误的,所以它们没有以正确的顺序显示在CollectionView中,显示消息的顺序CollectionView 在每次运行期间都会有所不同。我认为出现这个问题是因为每个加密消息完成解密过程所需的时间不同,有些加密消息需要更多时间来解密,有些加密消息比其他加密消息先完成解密,所以它们添加到 Messages 数组的顺序是不再正确。我期望的工作流程:

  1. 向 Firebase 数据库上的消息节点发出 fetch 请求
  2. 对于每条获取的消息:
    3.1。解密它
    3.2。将其附加到 Messages 数组
    3.3。重新加载 CollectionView 以更新 UI

但我不知道如何使用 GCD 来正确实现这一点,因为并发问题,显示消息的顺序不正确。但是,我找到了一个解决方案,如果我尝试在我的代码中放置 sleep(1) 命令,代码可以正常运行,但是由于 sleep 命令,它太慢了。我尝试了很多方法,但似乎都不对,除了使用sleep(1) 命令。请帮助我正确执行此操作,非常感谢!这是我的代码:

func observeMessage(){ 
        self.eThree = VirgilHelper.sharedVirgilHelper.eThreeToUse!

        // Get current user's UID
        guard let uid = FIRAuth.auth()?.currentUser?.uid , let toId = self.user?.id else {
            return;
        }

        let userMessagesRef = FIRDatabase.database().reference().child("user-messages").child(uid).child(toId);
        userMessagesRef.observe(.childAdded, with: { (snapshot) in

            let messageId = snapshot.key;
            let messagesRef = FIRDatabase.database().reference().child("messages").child(messageId);
            // Observe the entire value of that node
            messagesRef.observeSingleEvent(of: .value, with: { (snapshot) in

                if let dictionary = snapshot.value as? [String:AnyObject] {

                //sleep(1) // The working sleep command, but it's too slow

                let message = Message(dictionary: dictionary)
                    if let fromUID = message.fromId, let toUID = message.toId, let cipherText = message.text {

                        self.eThree!.lookupPublicKeys(of: [fromUID], completion: { (lookupResult, error) in
                            if error != nil {
                                print("Error when looking up the Public Key of UID \(fromUID), \(String(describing: error))")
                            }
                            if let lookupResult = lookupResult {
                                message.text = try! self.eThree!.decrypt(text: cipherText, from: lookupResult[fromUID]!)
                                print("text: \(message.text)")

                                // The concurency prolem happens at here
                                self.messages.append(message);

                                // Go back to main thread to update UI
                                DispatchQueue.main.async {
                                    // The concurency prolem happens at here, UI doesn't display with correct order of fetched-decrypted messages
                                    self.collectionView?.reloadData()

                                    let indexPath = IndexPath(item: self.messages.count-1, section: 0)
                                    self.collectionView?.scrollToItem(at: indexPath, at: .bottom, animated: true);
                                }
                            }

                        })
                    }
                }

            }, withCancel: nil)
        }, withCancel: nil)

    }

【问题讨论】:

    标签: swift firebase asynchronous grand-central-dispatch


    【解决方案1】:

    在 Swift 中,要等待其他任务完成后再继续执行其他任务,可以使用 DispatchQueue.group()。

    let group = DispatchGroup()
    
    group.enter()
    DispatchQueue.main.async {
        print("1")
        group.leave()
    }
    
    group.enter()
    DispatchQueue.main.async {
        print("2")
        group.leave()
    }
    
    group.enter()
    DispatchQueue.main.async {
        print("3")
        group.leave()
    }
    
    group.notify(queue: .main) {
        print("Done")
    }
    

    所以你使用它的方式:

    1. 初始化组
    2. 在开始任务之前输入组:group.enter()
    3. 在每个任务之后放置:group.leave()
    4. 将关闭传递给 group.notify。组任务为空时执行。

    注意: 多个 .enter() 需要与 .leave() 匹配

    【讨论】:

    • DispatchGroup 不保证任务按照调用enter() 的顺序完成,它只保证在所有任务完成后调用notify
    • @vadian true,但总是在组中剩下的所有任务之后调用 notify。因此,也许您可​​以制作以顺序为键并以相应消息为值的哈希映射。然后按顺序追加它们。
    • 您好 Hikaru,非常感谢您的回答!我尝试使用 DispatchGroup,但是这两个 Firebase 函数调用是并发的:userMessagesRef.observe(.childAdded, with: { (snapshot) inmessagesRef.observeSingleEvent(of: .value, with: { (snapshot) in 所以我想知道, group.enter()group.leave()group.notify()应该放在哪里才能让这些任务正常运行,因为一次有很多并发函数调用
    • 我会把 group.notify() 放在关闭后。如果它过早触发,那么可能会在这里延迟一些毫秒。对于 enter,将其放在调用并发块之前。离开,把它放在并发处理程序的末尾。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-07-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-13
    • 1970-01-01
    • 2017-09-12
    相关资源
    最近更新 更多