swift - 如何在 Swift 中创建自定义完成 block

标签 swift multithreading asynchronous

我正在尝试进行一些后期处理

a.一旦我的一个异步函数完成

b.一旦我所有的异步函数完成。

不幸的是,我在下面的代码中遇到了竞争条件。

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

最佳答案

说实话,我不太明白你在做什么。但是,我总是使用计数器变量来确定所有异步函数是否完成。

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

关于swift - 如何在 Swift 中创建自定义完成 block ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36275314/

相关文章:

xcode - 使用动态标签的 XC UITesting

swift - 单击 UITableView 时没有任何反应(尝试在 View 之间发送数据)

multithreading - 长时间运行的后台线程导致 HTTP 请求超时

.net - 对于异步 TCP 监听器,此模式是否正确?

c# - 如何异步调用两个方法?

error-handling - “NSError”无法转换为 'AutoreleasingUnsafePointer<NSError?>'

ios - ContiguousArrayStorage 没有实现 methodSignatureForSelector

c++ - 互锁函数 c++

php - 在 PHP 中启动新进程/线程的最简单方法

javascript - 我如何等到异步循环完成后才发送快速响应?