我有多线程的麻烦。情况如下:
我向后端发出异步请求,在某些情况下,需要取消这些请求。取消请求发生在一个单独的线程中。对后端的所有请求都将被取消,当我退出屏幕时,将释放多个类实例。
当我有订单请求 - 取消时,一切正常。但是,当我已经处于完成方法的中间时(由于数据的解码和转换需要一点时间),有时会调用cancel方法。在这种情况下,应用程序崩溃,发送到解除分配的实例的消息。它不是一个可以轻松缓存的例外,即使在我之前检查实例是否存在时,我也会遇到崩溃。实际上,如果我理解正确,则将取消分配finish和cancel方法所在类的实例。没关系,问题是线程在完成方法的中间切换,我需要防止它。
我的问题是:有没有办法阻止在方法中间切换线程?要将此方法声明为整体(事务)?或者是否存在针对此类问题的另一种标准解决方案?
我看过this post,但我真的不明白它是否可以用于我的情况或如何使用。
编辑:取消
for (XXX *request in [XXX sharedQueue].operations)
{
[request setDelegate:nil];
[request setDownloadProgressDelegate:nil];
[request setUploadProgressDelegate:nil];
[request setQueue:nil];
[request cancel];
}
XXX
是用于请求的类。
方法的顺序
以下是调用方法的顺序(在出错的情况下)。 Handler
是处理请求的类。
Handler 1: make request
Handler 1: finish begin
Handler 2: cancel begin
Handler 2: cancel end
Handler 2: dealloc
Handler 1: dealloc
Handler 1: finish middle
处理程序1和2是该类的两个实例。第一个是在完成方法的中间解除分配,所以在这结束时我得到了崩溃。取消分配是正常的,因为在取消后我转到另一个视图,基本上所有内容都被取消分配。
我对解决方案的想法是以某种方式阻止返回结束方法或在切换线程之前执行整个结束方法。不幸的是,我不知道如何实现其中之一。
答案 0 :(得分:2)
以下方法可能会对您有所帮助:
为您的控制器添加一个管理所有请求的标志;
输入请求完成块时,请执行以下操作:
completionBlock:^() {
@synchronized(self.myFinishFlag) {
...
}
}
是这样的:
-(void)userCancelledRequests:(id)sender {
@synchronized(self.myFinishFlag) {
...
}
}
这会延迟执行' userCancelledRequests body if a finish block is currently running (i.e., locking
self.myFinishFlag`)。
希望这有帮助。
答案 1 :(得分:2)
似乎未正确实施类cancel
的{{1}}方法。
假设有一些XXX
类型的异步操作响应XXX
消息。为了运行可靠,必须满足以下要求:
可以从任何主题从客户端发送cancel
消息。
可以在任何时间和多次时间发送cancel
消息。
当操作收到cancel
消息时,它会停止异步任务并在下一个“取消点”正确清理它。注意:这可能与cancel
方法异步发生。实现需要“线程安全”!
接收方应通知代表已被取消(例如在失败处理程序中)。
此外,在发送cancel
消息之前,呼叫者不需要以任何方式重置代理人或准备接收者。
这些要求需要通过类cancel
的实现来实现。
现在,假设您有一个内部XXX
方法用于该操作,并假设这是操作将执行的最后一个方法。执行该方法时,以及当操作同时收到finish
消息 时,实现必须保证cancel
无效,因为它 < / em>迟到:取消操作的最后机会已经过去了。有很多方法可以实现这一目标。
如果是这样,那么以下代码应该正确取消您的操作:
cancel
for (XXX *request in [XXX sharedQueue].operations) {
[request cancel];
}
和cancel
方法的“NSOperation like”实现示例:
注意:
访问 ivars 必须同步! Ivars将以各种内部方法访问。所有访问都将通过名为“sync_queue”的私有串行调度队列进行序列化。
finish
答案 2 :(得分:0)
阻止的最佳方法是在专用队列上执行所有操作。如果您想取消,请执行以下操作:
dispatch_async(_someQueue, ^{
for (XXX *request in [XXX sharedQueue].operations)
{
[request setDelegate:nil];
[request setDownloadProgressDelegate:nil];
[request setUploadProgressDelegate:nil];
[request setQueue:nil];
[request cancel];
}
});
然后,当触发完成的回调时,您可以执行以下操作:
dispatch_async(_someQueue, ^{
if ([request isCancelled] == NO)
{
// Process request
}
});
只要_someQueue没有标记为并发队列,这应该可以解决问题。