异步操作无法按顺序完成

时间:2016-05-10 23:02:54

标签: ios swift asynchronous grand-central-dispatch

我有以下快速代码,允许将用户联系人映射到网络服务器,然后在tableview中显示:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {


    dispatch_group_enter(self.dispatch_group)
    dispatch_group_async(self.dispatch_group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
         print("start 1")

        self.contacts = self.findContacts()

        dispatch_group_leave(self.dispatch_group)
        print("End 1")
    }

    dispatch_group_enter(self.dispatch_group)
    dispatch_group_async(self.dispatch_group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
        print("start 2")

        // Method to match contacts with Firebase db:
        self.mapContactsToPresentFriends(self.contacts, firebaseMainURL: self.firebaseMainURL, usersSyncID: "fdb17f3a-7b7d-4aa5-9a0b-b9fb33c349de")

        dispatch_group_leave(self.dispatch_group)
        print("End 2")
    }


    dispatch_group_notify(self.dispatch_group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), {
        print("start 3")

        // Final load of who is left of conacts (not user or friend)
        for contact in self.contacts {

            let phoneNumber = contact.phoneNumbers[0].value as! CNPhoneNumber

            self.friends.append(Friend(userName: "", phoneNumber: phoneNumber.stringValue, status: 0, name: contact.givenName, userID: ""))
        }

        for friend in self.friends {
            print(friend.status)
        }

        self.tableView!.reloadData()

    })
}

但是,注意打印(“开始1”),打印(“开始2”)和打印(“开始3”)语句,日志显示执行:

start 2
start 1
End 2
End 1
start 3

会产生不准确的结果。我需要按顺序发生这些异步任务(如print语句所示),否则结果将没有意义。

如何重新安排以确保其有效?

2 个答案:

答案 0 :(得分:2)

如果您真的希望这些事情按顺序发生,则不需要单独的块。

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
    print("start 1")
    let contacts = self.findContacts()
    print("End 1")

    print("start 2")
    // Method to match contacts with Firebase db:
    self.mapContactsToPresentFriends(contacts, firebaseMainURL: self.firebaseMainURL, usersSyncID: "fdb17f3a-7b7d-4aa5-9a0b-b9fb33c349de")
    print("End 2")

    print("start 3")
    // Final load of who is left of conacts (not user or friend)
    var friends: [Friend] = []
    for contact in contacts {

        let phoneNumber = contact.phoneNumbers[0].value as! CNPhoneNumber

        friends.append(Friend(userName: "", phoneNumber: phoneNumber.stringValue, status: 0, name: contact.givenName, userID: ""))
    }

    for friend in friends {
        print(friend.status)
    }

    dispatch_async(dispatch_get_main_queue()) {
        // Any data/UI updates MUST be on the main queue
        self.friends = friends
        self.tableView!.reloadData()
    }
}

另外几件事要注意:

  • 使用dispatch_group_async取代了对_enter()_leave()的需求。你不需要两者。 (如果您确实想要使用群组,则还需要dispatch_group_notify来完成工作。)
  • 当您单独调度每个块时,不需要外部调度(因为调用_async()很快)。
  • self.tableView.reloadData()(和UI更新)必须在主线程上发生,因此,您的数据源(例如self.friends)也必须在主线程上更新。我在上面的代码段中对此进行了更改。

答案 1 :(得分:1)

您应该创建一个串行队列(使用带有DISPATCH_QUEUE_SERIAL属性的dispatch_queue_create函数。)

然后将您的任务提交到串行队列。它们仍将与主线程同时运行,但串行队列中的每个任务在下一个任务开始之前完成。

您还可以在开始下一个任务之前在您希望完成上一个任务的每个点设置障碍,但听起来您希望所有任务按顺序运行,因此串行队列更适合并且更容易设置起来。