2009-12-15 63 views
3

我在运行一组NSOperations(最大并发设置为1)的主线程上有一个NSOperationQueue,我希望能够在任何时候取消时间。当我按下一个按钮时,我告诉队列取消所有操作并等待直到完成。这应该挂起主线程,直到操作队列为空,但是它无限期地挂起我的主线程。主线程无限期挂起,等待NSOperationQueue操作取消[Only on Device!]

这里是我用来阻止它的代码:

... 
[myQueue cancelAllOperations]; 
[myQueue waitUntilAllOperationsAreFinished]; 
return YES; // This line never gets called 

注:我需要使用waitUntilAllOperationsAreFinished作为进一步处理要求队列为空。

奇怪的是这只发生在设备上。在模拟器中运行时,按预期工作。

我看过断点,我可以按照当前正在运行的操作,直到它完成。它会检测[self isCancelled],停止正在执行的操作并将其拖动到main方法的末尾。我可以看到操作中没有任何操作导致它挂起,并且通过取消所有操作,其他任何操作都不应启动,并且队列应该结束。我通过添加断点来检查并且其他操作都没有开始。

这是怎么发生的?

回答

7

在您的任何操作(或任何其他线程)中,您使用的是-performSelectorOnMainThread:withObject:waitUntilDone:-waitUntilAllOperationsAreFinished将阻止任何被调用的线程,直到所有操作完成。可能性是,如果您在应用程序终止时调用此线程,该线程将成为主线程。如果主线程被阻塞,并且其中一个操作使用-performSelectorOnMainThread:withObject:waitUntilDone:,则应用程序将冻结,因为操作永远不会完成。

我以前有过这种事情发生在我身上。不幸的是,这很难解决。

+0

奇怪的是,这是发生在非并发操作,不会在主线程上进行任何调用。不过,我有一个并发操作,在异步调用之间的主线程上执行某些操作,我认为这会被阻止!我不明白为什么这种方法可用,如果它可能导致这些问题。除非它的意思是在另一个不是主线程的线程上调用!?我试图取消我的应用程序终止操作,当它挂起时,由于终止时间而导致崩溃报告。相当棘手的一个!有什么建议么?感谢您的回答! – 2009-12-16 03:05:58

+0

我处理过的一种方法是取消所有会触发主线程的后台进程,然后调用-cancelAllOperations。在任何将在我的操作中的主线程上执行选择器的代码之前,我会添加一个isCancelled检查,如果该检查为true,那么将退出该操作。然后在-cancelAllOperations消息后稍后sleep(),最后调用-waitUntilAllOperationsAreFinished。如果你小心地关闭所有碰到主线程的东西,但是这个,你可以做这个工作。 – 2009-12-16 13:42:05

+0

感谢Brad,这几乎是我克服它的方式。 'cancelAllOperations'然后休眠〜1s(当然!)。我的'NSOperations'检查'isCancelled'很快,在任何可能需要一段时间的事情之前,所以他们应该很快退出。另一个小技巧,我发现我的核心数据保存需要一段时间,我已经使用了一个全球NSCondition,所以我基本上取消,睡眠,等待信号(如果需要)。这完美地保持了主线,像梦一样工作!谢谢你的帮助! – 2009-12-17 10:01:05

0

也许waitUntilAllOperationsArefFinished正在导致该块...也许这些操作都会在调用waitUntilAllOperationsArefFinished之前取消并完成,然后队列处于挂起状态,等待已完成的操作完成...?

我不知道这个事实,但也许尝试不要打电话waitUntilAllOperationsArefFinished

4

你永远不应该阻止主线程。它处理所有UI更新的一件事,另一件事如您注意到您设法造成了死锁。

相反,尝试建立类似的方法:

- (void) notifyOnFinish 
{ 
    [myQueue waitUntilAllOperationsAreFinished]; 
    [self performSelectorOnMainThread:(queueEmpty) withObject:nil waitUntilDone:NO]; 
} 

那么,你现在有你的代码,请致电:

[myQueue cancelAllOperations]; 
[self performSelectorInBackground:@selector(notifyOnFinish) withObject:nil]; 

而且在queueEmpty方法,你只需做你想做的事时,队列被清空。

基本上,只需创建一个后台线程来阻塞而不是主线程。

+1

我需要阻止主线程,因为操作在应用终止时被取消,我需要确保它们正确关闭。他们会很快取消,所以没有太长的问题。所以我需要阻止主线程,以确保后台线程不仅仅是在app终止时随机停止。 – 2009-12-16 06:44:28

+0

在退出时阻塞主线程是我可以考虑可能需要的一种情况,以确保在应用程序关闭之前完成所有操作。在单独的线程上回调不会削减它,因为这不会阻止应用程序在操作中停止。 – 2009-12-16 13:36:00

+0

然后,您需要消除任何死锁的可能性,我的想法将是主线程在阻塞之前设置的全局同步标志,并且在尝试在主线程上执行选择器之前,所有操作都会进行检查。 – 2009-12-16 17:55:03