Asynchronous actions do not complete in order

2019-09-02 06:34发布

I have the following swift code to allow for mapping of a users contacts to the webserver, and then show in the 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()

    })
}

However, noting the print("Start 1"), print("Start 2") and print("Start 3") statements, the log displays the execution as:

start 2
start 1
End 2
End 1
start 3

Which yields an inaccurate result. I need these asynchronous tasks to occur in order (as indicated by the print statements), or else the result will not make sense.

How can this be rearranged to ensure that it works?

2条回答
对你真心纯属浪费
2楼-- · 2019-09-02 06:43

You should probably create a serial queue (use the dispatch_queue_create function with the DISPATCH_QUEUE_SERIAL attribute.)

Then submit your tasks to the serial queue. They will still run concurrently with the main thread, but each task in a serial queue completes before the next one starts.

You could also set up barriers at each point where you wanted the previous task to complete before beginning the next, but it sounds like you want ALL of your tasks to run sequentially so a serial queue is a better fit and easier to set up.

查看更多
Bombasti
3楼-- · 2019-09-02 06:57

If you really want these things to happen in order, there's no need for separate blocks.

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

Another couple things to note:

  • Using dispatch_group_async replaces the need for _enter() and _leave(). You don't need both. (If you had really wanted to use groups, you'd also need dispatch_group_notify to complete the work.)
  • When you were dispatching each block separately, there's no need for an outer dispatch (since calling _async() is fast).
  • self.tableView.reloadData() (and UI updates) must happen on the main thread, and consequentially, your data source (e.g. self.friends) must also be updated on the main thread. I've changed this in the snippet above.
查看更多
登录 后发表回答