当到期处理程序被调用时(在主线程上同步调用),我们必须快速完成/取消任务,以防止应用程序被终止。
我在某个地方读到(但找不到引用),所有的处理必须在过期块返回时完全完成。
这是否意味着,如果我使用的是一个NSOperationQueue,我必须取消,然后等待操作完成后才返回?如下所示:
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可能需要一小段时间(甚至一两秒钟)才能正确取消,那么我们如何能够安全地确保在调用到期处理程序时它真的完成了,而没有意外地死锁呢?
发布于 2015-10-12 19:11:32
当在主线程上调用operationQueue.waitUntilAllOperationsAreFinished()时,它将被阻塞,而委托或其他从其他线程调度到主线程的代码将导致死锁。
这是一个罕见的情况,你必须“繁忙等待”和投票(线程安全!)表示任务已经完成的某种标志。幸运的是,您可以在不过分强调RunLoop的情况下利用CPU。通过运行循环对主线程执行“等待”,您还可以实际执行主线程上调度的委托或连续,而不会陷入死锁。
下面的代码(使用Swift)展示了如何使用run循环来完成主线程上的“繁忙等待”,以及在主线程上执行的任务:
假设您有一些表示异步任务的最终结果的“未来”:
public protocol Future : class {
var isCompleted: Bool { get }
func onComplete(f: ()->())
}理想情况下,您可以“注册”(一个或多个)完成处理程序,这些处理程序在将来完成时会被调用。
现在,我们可以为将来定义一个方法runloopWait,它在线程上“等待”(必须有一个RunLoop),直到将来完成为止:
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循环只在调用完成处理程序时才返回。也就是说,您实际上不必忙着等待,而是某种异步等待。
下面的代码以模拟未来的方式完成了示例,您可以运行它来检查上面的解决方案。请注意,将来的实现并不是线程安全的,而是应该在本例中工作。
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")https://stackoverflow.com/questions/33083743
复制相似问题