异步操作未按顺序完成
Asynchronous actions do not complete in order
我有以下 swift 代码允许将用户联系人映射到网络服务器,然后显示在表视图中:
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()
})
}
但是,注意 print("Start 1")、print("Start 2") 和 print("Start 3") 语句,日志将执行显示为:
start 2
start 1
End 2
End 1
start 3
这会产生不准确的结果。我需要这些异步任务按顺序发生(如打印语句所示),否则结果将毫无意义。
如何重新安排以确保其有效?
如果您真的希望这些事情按顺序发生,则不需要单独的块。
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
)也必须在主线程上更新。我已经在上面的代码片段中更改了它。
您或许应该创建一个串行队列(使用带有 DISPATCH_QUEUE_SERIAL 属性的 dispatch_queue_create 函数。)
然后将您的任务提交到串行队列。它们仍将 运行 与主线程并发,但串行队列中的每个任务都会在下一个任务开始之前完成。
您还可以在希望上一个任务完成后再开始下一个任务的每个点设置障碍,但听起来您希望所有任务按顺序 运行 所以串行队列是更合身且更易于设置。
我有以下 swift 代码允许将用户联系人映射到网络服务器,然后显示在表视图中:
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()
})
}
但是,注意 print("Start 1")、print("Start 2") 和 print("Start 3") 语句,日志将执行显示为:
start 2
start 1
End 2
End 1
start 3
这会产生不准确的结果。我需要这些异步任务按顺序发生(如打印语句所示),否则结果将毫无意义。
如何重新安排以确保其有效?
如果您真的希望这些事情按顺序发生,则不需要单独的块。
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
)也必须在主线程上更新。我已经在上面的代码片段中更改了它。
您或许应该创建一个串行队列(使用带有 DISPATCH_QUEUE_SERIAL 属性的 dispatch_queue_create 函数。)
然后将您的任务提交到串行队列。它们仍将 运行 与主线程并发,但串行队列中的每个任务都会在下一个任务开始之前完成。
您还可以在希望上一个任务完成后再开始下一个任务的每个点设置障碍,但听起来您希望所有任务按顺序 运行 所以串行队列是更合身且更易于设置。