首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >后台任务过期处理程序和NSOperationQueue (我们需要等待操作完成吗?)

后台任务过期处理程序和NSOperationQueue (我们需要等待操作完成吗?)
EN

Stack Overflow用户
提问于 2015-10-12 14:31:51
回答 1查看 638关注 0票数 1

当到期处理程序被调用时(在主线程上同步调用),我们必须快速完成/取消任务,以防止应用程序被终止。

我在某个地方读到(但找不到引用),所有的处理必须在过期块返回时完全完成。

这是否意味着,如果我使用的是一个NSOperationQueue,我必须取消,然后等待操作完成后才返回?如下所示:

代码语言:javascript
复制
backgroundTaskIdentifier = UIApplication.sharedApplication().beginBackgroundTaskWithName("MyBackgroundTask") {
    operationQueue.cancelAllOperations()
    operationQueue.waitUntilAllOperationsAreFinished() // Wait, so everything finishes before returning from this func (but might deadlock!)
    UIApplication.sharedApplication().endBackgroundTask(backgroundTaskIdentifier)
}

这意味着锁定主线程,这可能导致一些需要后台线程死锁的任务。

取消NSOperation可能需要一小段时间(甚至一两秒钟)才能正确取消,那么我们如何能够安全地确保在调用到期处理程序时它真的完成了,而没有意外地死锁呢?

EN

回答 1

Stack Overflow用户

发布于 2015-10-12 19:11:32

当在主线程上调用operationQueue.waitUntilAllOperationsAreFinished()时,它将被阻塞,而委托或其他从其他线程调度到主线程的代码将导致死锁。

这是一个罕见的情况,你必须“繁忙等待”和投票(线程安全!)表示任务已经完成的某种标志。幸运的是,您可以在不过分强调RunLoop的情况下利用CPU。通过运行循环对主线程执行“等待”,您还可以实际执行主线程上调度的委托或连续,而不会陷入死锁。

下面的代码(使用Swift)展示了如何使用run循环来完成主线程上的“繁忙等待”,以及在主线程上执行的任务:

假设您有一些表示异步任务的最终结果的“未来”:

代码语言:javascript
复制
public protocol Future : class {        
    var isCompleted: Bool { get }
    func onComplete(f: ()->())        
}

理想情况下,您可以“注册”(一个或多个)完成处理程序,这些处理程序在将来完成时会被调用。

现在,我们可以为将来定义一个方法runloopWait,它在线程上“等待”(必须有一个RunLoop),直到将来完成为止:

代码语言:javascript
复制
extension Future {

    public func runLoopWait() {
        // The current thread MUST have a run loop and at least one event source!
        // This is difficult to verfy in this method - thus this is simply
        // a prerequisite which must be ensured by the client. If there is no
        // event source, the run lopp may quickly return with the effect that the
        // while loop will "busy wait".

        var context = CFRunLoopSourceContext()
        let runLoop = CFRunLoopGetCurrent()
        let runLoopSource = CFRunLoopSourceCreate(nil, 0, &context)

        CFRunLoopAddSource(runLoop, runLoopSource, kCFRunLoopDefaultMode)
        self.onComplete() {
            CFRunLoopStop(runLoop);
        }

        while !self.isCompleted {
            CFRunLoopRun()
        }

        CFRunLoopRemoveSource(runLoop, runLoopSource, kCFRunLoopDefaultMode)
    }

}

使用此代码,run循环只在调用完成处理程序时才返回。也就是说,您实际上不必忙着等待,而是某种异步等待。

下面的代码以模拟未来的方式完成了示例,您可以运行它来检查上面的解决方案。请注意,将来的实现并不是线程安全的,而是应该在本例中工作。

代码语言:javascript
复制
public class Task : Future {

    var _completed = false

    let _task: () -> ()
    var _continuation: ()->()

    public init(task: () -> ()) {
        _task = task
        _continuation = {}
    }

    public var isCompleted: Bool {
        return _completed
    }

    public static func start(t: Task, queue: dispatch_queue_t ) {
        dispatch_async(queue) {
            t._task()
            t._completed = true
            t._continuation()
        }
    }

    public func onComplete(f:()->()) {
        _continuation = f
    }

}



let t = Task() {

    for _ in 0...5 {
        sleep(1)
        print(".", terminator: "")
    }
    print("")
}

// run the task on the main queue:
Task.start(t, queue: dispatch_get_main_queue())

t.runLoopWait() // "wait" on the main queue
print("main thread finished")
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/33083743

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档