我正在尝试做一些后期处理
一个。一旦我的一个异步函数完成了
湾一旦我的所有异步功能都完成了。
不幸的是,我在下面的代码中遇到了竞争条件。
func foo(stuff: AnArrayOfObjects, completed: (NSError?)->()) {
// STEP 1 OF CREATING AN OVERALL COMPLETION BLOCK: Create a dispatch group.
let loadServiceGroup: dispatch_group_t = dispatch_group_create()
// Define errors to be processed when everything is complete.
// One error per service; in this example we'll have two
let configError: NSError? = nil
let preferenceError: NSError? = nil
// some more preprocessing / variable declarations here. E.g.:
var counter = 0
// STEP 2 OF CREATING AN OVERALL COMPLETION BLOCK: Adding tasks to a dispatch group
dispatch_group_enter(loadServiceGroup)
// here i may start MULTPILE functions that are asynchronous. For example:
for i in stuff {
estore.fetchRemindersMatchingPredicate(remindersPredicate) {
// MARK: Begininning of thread
// does something here. E.g., count the elements:
counter += 1
// update the UI
dispatch_async(dispatch_get_main_queue()) {
self.sendChangedNotification() // reloads the tableview.
// WARNING: I CAN'T JUST SHOW THE counter RESULTS HERE BECAUSE IT MIGHT NOT BE DONE YET. IT IS ASYNCHRONOUS, IT MIGHT STILL BE RUNNING.
}
}
// STEP 3 OF CREATING AN OVERALL COMPLETION BLOCK: Leave dispatch group. This must be done at the end of the completion block.
dispatch_group_leave(loadServiceGroup)
// MARK: End of thread
}
// STEP 4 OF CREATING AN OVERALL COMPLETION BLOCK: Acting when the group is finished
dispatch_group_notify(loadServiceGroup, dispatch_get_main_queue(), {
// do something when asychrounous call block (above) has finished running. E.g.:
print("number of elements: \(counter)")
// Assess any errors
var overallError: NSError? = nil;
if configError != nil || preferenceError != nil {
// Either make a new error or assign one of them to the overall error.
overallError = configError ?? preferenceError
}
// Call the completed function passed to foo. This will contain additional stuff that I want executed in the end.
completed(overallError)
})
}
答案 0 :(得分:0)
说实话,我并不完全明白你在做什么。但是,我总是使用计数器变量来确定是否所有异步函数都已完成。
func foo(completionHandler: (success: Bool) -> Void) {
var numberOfTasks = 0
for data in datasource {
// do something preparing
numberOfTasks += 1
}
var numberOfDones = 0
objc_sync_enter(numberOfDones)
data.foo(completionHandler:(){
// do something handling outcome
numberOfDones += 1
if numberOfDones == numberOfTasks {
completionHandler(true)
}
})
objc_sync_exit(numberOfDones)
}
机制是我们知道要完成的任务总数,并且对于每个任务我们都可以捕获完成事件,因此我们添加了numberOfDones
。因此,每当numberOfDones == numberOfTasks
我们都知道这是最后一个,并且已经完成了。
根据您的代码,我尝试将此想法应用于此。
func foo(stuff: AnArrayOfObjects, completed: ([NSError]?)->()) {
// STEP 1 OF CREATING AN OVERALL COMPLETION BLOCK: Create a dispatch group.
let loadServiceGroup: dispatch_group_t = dispatch_group_create()
// Define errors to be processed when everything is complete.
// One error per service; in this example we'll have two
let configError: NSError? = nil
let preferenceError: NSError? = nil
// some more preprocessing / variable declarations here. E.g.:
var counter = 0
// STEP 2 OF CREATING AN OVERALL COMPLETION BLOCK: Adding tasks to a dispatch group
dispatch_group_enter(loadServiceGroup)
// here i may start MULTPILE functions that are asynchronous. For example:
for i in stuff {
estore.fetchRemindersMatchingPredicate(remindersPredicate) {
// MARK: Begininning of thread
// does something here. E.g., count the elements:
counter += 1
// update the UI
dispatch_async(dispatch_get_main_queue()) {
self.sendChangedNotification() // reloads the tableview.
// WARNING: I CAN'T JUST SHOW THE counter RESULTS HERE BECAUSE IT MIGHT NOT BE DONE YET. IT IS ASYNCHRONOUS, IT MIGHT STILL BE RUNNING.
}
}
// STEP 3 OF CREATING AN OVERALL COMPLETION BLOCK: Leave dispatch group. This must be done at the end of the completion block.
dispatch_group_leave(loadServiceGroup)
// MARK: End of thread
}
var numberOfDones = 0
var errors = [NSError]()
// STEP 4 OF CREATING AN OVERALL COMPLETION BLOCK: Acting when the group is finished
objc_sync_enter(numberOfDones)
dispatch_group_notify(loadServiceGroup, dispatch_get_main_queue(), {
// do something when asychrounous call block (above) has finished running. E.g.:
print("number of elements: \(counter)")
// Assess any errors
var overallError: NSError? = nil;
if configError != nil || preferenceError != nil {
// Either make a new error or assign one of them to the overall error.
errors.append(configError ?? preferenceError!)
}
numberOfDones += 1
if numberOfDones == counter {
// Call the completed function passed to foo. This will contain additional stuff that I want executed in the end.
completed(errors)
}
})
objc_sync_exit(numberOfDones)
}
修改强>
感谢@CouchDeveloper对我的回答发表了评论。我找到了另一个通过同步计数器变量使线程安全的解决方案。答案已更新,以下是解决方案的实验
https://gist.github.com/qiuyujx/7173ea663308cc03f07e8a5c09cf4cba