【问题标题】:Main thread hangs indefinitely while waiting for NSOperationQueue operations to cancel [Only on Device!]主线程在等待 NSOperationQueue 操作取消时无限期挂起 [仅在设备上!]
【发布时间】:2010-12-27 00:59:44
【问题描述】:

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

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

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

注意:我需要使用waitUntilAllOperationsAreFinished,因为后续进程要求队列为空。

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

我已经观察了断点,我可以跟踪当前正在运行的操作,直到它完成。它检测到 [self isCancelled],停止它正在做的事情,然后一直运行到 main 方法的末尾。我可以看到操作中没有任何内容导致它挂起,并且通过取消所有操作,其他操作都不应该开始,队列应该完成。我已经通过添加断点进行了检查,并且没有其他操作开始。

为什么会这样?

【问题讨论】:

    标签: iphone multithreading nsoperation


    【解决方案1】:

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

    我以前也遇到过这样的事情。不幸的是,这很难解决。

    【讨论】:

    • 奇怪的是,这发生在一个不对主线程进行任何调用的非并发操作上。但是,我有一个并发操作,它在异步调用之间的主线程上执行操作,我认为这被阻塞了!如果可能导致这些问题,我真的不明白为什么可以使用此方法。除非它打算在另一个不是主线程的线程上调用!?我正在尝试取消我对应用程序终止的操作,当它挂起时,由于终止时间而导致崩溃报告。相当棘手的一个!有什么建议?感谢您的回答!
    • 我处理这个问题的一种方法是取消所有会命中主线程的后台进程,然后调用-cancelAllOperations。在我的操作中将在主线程上执行选择器的任何代码之前,我添加了对 isCancelled 的检查,如果为真,它将在该点退出操作。然后我在 -cancelAllOperations 消息之后 sleep() 一小会儿,最后调用 -waitUntilAllOperationsAreFinished。如果你小心关闭所有命中主线程的东西,但这个,你可以让它工作。
    • 谢谢布拉德,这就是我克服它的方法。 cancelAllOperations 之后是睡眠约 1 秒(可以肯定!)。我的NSOperations 检查isCancelled ALOT,在任何可能需要一段时间之前,所以他们应该很快退出。我发现的另一个小技巧是我的核心数据保存需要一段时间,我使用了全局 NSCondition,所以我基本上取消、睡眠、等待信号(如果需要)。这完美地保持了主线程,像梦一样工作!感谢您的帮助!
    • 我遇到了完全相同的问题。这是我解决它的方法:我没有调用 performSelectorOnMainThread: 我使用 [[NSOperationQueue mainQueue] addOperationWithBlock:^(void){ ... }];请注意,这不会等到块完成。
    【解决方案2】:

    你永远不应该阻塞主线程。它处理你所有的 UI 更新,一方面是因为你已经注意到你设法创建了一个死锁。

    请尝试创建如下方法:

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

    那么你现在有你的代码,调用:

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

    在 queueEmpty 方法中,当队列被清空时,您只需执行您想做的任何事情。

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

    【讨论】:

    • 我需要阻塞主线程,因为操作在应用程序终止时被取消,我需要确保它们正确关闭。他们会很快取消,所以不存在花费太长时间的问题。所以我需要阻塞主线程以确保后台线程不会只是在应用程序终止时随机停止。
    • 在退出时阻塞主线程是我能想到的一种情况,以保证在应用程序关闭之前您的所有操作都将完成。单独线程上的回调不会切断它,因为这不会阻止应用程序在运行中停止。
    • 那么你需要消除任何死锁的可能性,我的想法是主线程会在阻塞之前设置一个全局同步标志,并且所有操作都会在尝试在主线程上执行选择器之前进行检查线程。
    • 嗨 Kendall,我创建了另一个 NSOperationQueue 来运行 notifyOnFinish,我应该调用 performSelectorOnMainThread 吗?因为 UI 没有更新。
    【解决方案3】:

    也许waitUntilAllOperationsArefFinished 导致了阻塞...也许操作在调用waitUntilAllOperationsArefFinished 之前全部取消并完成,然后队列挂起,等待已经完成的操作完成...?

    我不知道这个事实,但也许尽量不要打电话给waitUntilAllOperationsArefFinished

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-08-16
      • 2021-10-13
      • 1970-01-01
      • 2016-10-06
      • 2011-10-23
      • 2012-04-17
      • 2017-09-13
      • 2022-01-02
      相关资源
      最近更新 更多