在macOS应用程序中,我有一个后台线程用一行代码执行,需要花费大量时间.如何取消阻止



在我正在开发的Cocoa Objective-C应用程序中,我有一个NSTableView,它有一个实现tableViewSelectionDidChange的委托。在该方法的委托实现中,我放置了一个用GCD实现的后台串行队列,该队列负责将当前选定的对象加载到表中,以便它显示在表右侧的详细信息NSView中。我想要的行为是,如果背景线程还没有完成,并且左表中有一个新的选择,我想取消它,并为新的选择启动一个新线程。我在这里找到了一个使用GCD的非常优雅的解决方案,它使用:

dispatch_block_cancel(work);

问题是,正如链接中所说:

"需要注意的一点是,dispatch_block_cancel并不是先发制人的。如果工作程序块处于长时间运行的中间,则dispatch_block _cancel不会强制终止它。为此,我们必须定期使用dispatch_bock _testcancel测试取消。下面是一个示例:">

for (...) {
/* do some work */
[NSThread sleepForTimeInterval:0.2];
if (dispatch_block_testcancel(work) != 0) {
/* exit gracefully */
return;
}
}

问题是,在我的应用程序中,tableViewSelectionDidChange实现中执行的最长的操作是在一行代码中,我无法重构代码来更改这一点,因为该语句正在调用我正在使用的外部dylib。在这种情况下,我无法检查区块是否被取消:

if (dispatch_block_testcancel(work) != 0) {
/* exit gracefully */
return;
}

因为线程在冗长的单行操作中被阻塞。有人能有希望提出某种策略吗,最好还是使用GCD?我知道NSOperation和NSOperationQueue,但在学习使用它们之前,我想确定它们不会像GCD那样在1行语句中间取消操作。非常感谢你的帮助。

这是不可解的。使用哪种工具并不重要(GCD与NSOperation)。无法强制停止正在进行的操作。它会让记忆处于未知状态。解决方案是等待代码行完成,然后如果块被取消,则丢弃结果。如果这行代码非常昂贵,以至于您真的需要终止它(而不是忽略它的结果),那么您将不得不用检查取消标志的代码来替换库。

您可以将对dylib的调用重构为xpc服务调用,这将有效地在不同的进程中运行调用,从而允许您终止它。

我们不得不连接到一个有点脆弱和缓慢的第三方库,将它封装在xpc服务后面,使我们能够优雅地处理它的脆弱性。

https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingXPCServices.html

最新更新