这是两个部分的问题。希望有人能给我一个完整的答复。
NSOperation是强大的对象。它们可以有两种不同的类型:非并发的或并发的。
第一种类型同步运行。您可以通过将非并发操作添加到NSOperationQueue中来利用它们。后者为您创建一个线程。结果是以并发方式运行该操作。唯一的注意事项是这样一个操作的生命周期。当它的main方法完成时,它将从队列中移除。当您处理异步API时,这可能是一个问题。
那么并发操作呢?苹果公司的
如果您想实现一个并发操作--也就是说,一个针对调用线程异步运行的操作--您必须编写额外的代码来异步启动该操作。例如,您可能会生成一个单独的线程,调用一个异步系统函数,或者做任何其他事情来确保start方法启动任务并立即返回,并且很可能在任务完成之前返回。
这对我来说几乎是很清楚的。它们异步运行。但你必须采取适当的行动,以确保他们这样做。
我不清楚的是以下几点。医生说:
注意:在OSXv10.6中,操作队列忽略isConcurrent返回的值,并始终从单独的线程调用操作的开始方法。
它的真正含义是什么?,如果我在中添加一个并发操作,会发生什么?
然后,在本文中的并行操作中,并发操作通过NSURLConnection (以其异步形式)下载某些HTTP。操作是并发的,并包含在特定队列中。
UrlDownloaderOperation * operation = [UrlDownloaderOperation urlDownloaderWithUrlString:url];
[_queue addOperation:operation];由于NSURLConnection需要一个循环才能运行,所以作者将start方法分流到主线程中(因此我假设将操作添加到它生成的队列中)。以这种方式,主运行循环可以调用操作中包含的委托。
- (void)start
{
if (![NSThread isMainThread])
{
[self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
return;
}
[self willChangeValueForKey:@"isExecuting"];
_isExecuting = YES;
[self didChangeValueForKey:@"isExecuting"];
NSURLRequest * request = [NSURLRequest requestWithURL:_url];
_connection = [[NSURLConnection alloc] initWithRequest:request
delegate:self];
if (_connection == nil)
[self finish];
}
- (BOOL)isConcurrent
{
return YES;
}
// delegate method here...我的问题如下。线程安全吗?循环侦听源,但被调用的方法在后台线程中调用。我说错了吗?
编辑
我已经根据Dribin提供的代码完成了一些测试(参见1)。我注意到,正如您所写的,NSURLConnection的回调是在主线程中调用的。
好吧,但现在我还是很困惑。我会尽力解释我的疑虑。
为什么在并发操作中包含异步模式,在主线程中调用回调?将start方法分流到它允许在主线程中执行回调的主线程,队列和操作怎么办?我在哪里利用GCD提供的线程机制?
希望这是清楚的。
发布于 2013-01-11 23:51:17
这是一个很长的答案,但简短的版本是,您所做的工作是完全安全的,因为您迫使操作的重要部分在主线程上运行。
您的第一个问题是,“如果我在NSOperationQueue中添加一个并发操作会发生什么?”截至iOS 4,NSOperationQueue在幕后使用GCD。当您的操作到达队列顶部时,它将被提交到GCD,GCD管理一个私有线程池,该线程池根据需要动态地增长和收缩。GCD分配其中一个线程来运行您操作的start方法,并保证这个线程永远不会是主线程。
当start方法在并发操作中完成时,不会发生任何特殊情况(这就是要点)。队列将允许您的操作永远运行,直到您将isFinished设置为YES,并执行正确的KVO /didChange调用,而不管调用线程如何。通常,您会创建一个名为finish的方法来完成这一任务,它看起来就是这样的。
所有这些都很好,但是如果需要观察或操作正在运行操作的线程,则需要注意一些问题。需要记住的重要一点是:不要弄乱由GCD管理的线程。您不能保证它们将超过当前的执行框架,而且您也肯定不能保证后续的委托调用(即来自NSURLConnection)将发生在同一个线程上。事实上,他们可能不会。
在您的代码示例中,您已经将start分流到主线程,因此不需要太担心后台线程(GCD或其他线程)。当您创建一个NSURLConnection时,它会被安排在当前的run循环上,并且它的所有委托方法都将在那个run循环的线程上被调用,这意味着在主线程上启动连接保证它的委托回调也发生在主线程上。从这个意义上说,它是“线程安全”的,因为除了操作本身的开始之外,背景线程上几乎没有任何事情发生,这实际上是一个优势,因为GCD可以立即回收线程并将其用于其他事情。
让我们想象一下,如果您不强制start在主线程上运行,而只是使用GCD提供给您的线程,将会发生什么情况。如果线程消失,运行循环可能永远挂起,例如当GCD将其回收到其私有池时。有一些技术可以使线程保持活动状态(例如添加空的NSPort),但是它们不适用于GCD创建的线程,只适用于您自己创建的线程,并且可以保证线程的生存期。
这里的危险在于,在轻负载下,您实际上可以在GCD线程上运行一个运行循环,并认为一切都很好。一旦您开始运行许多并行操作,特别是当您需要在中途取消它们时,您将开始看到从未完成且从未释放的操作,从而泄漏内存。如果您想要完全安全,就需要创建自己的专用NSThread,并让run循环永远运行下去。
在现实世界中,做您正在做的事情并在主线程上运行连接要容易得多。管理连接占用的CPU非常少,而且在大多数情况下不会干扰您的UI,因此完全在后台运行连接几乎没有什么好处。主线程的run循环总是在运行,您不需要处理它。
但是,可以使用上面描述的专用线程方法在后台完全运行NSURLConnection连接。例如,查看JXHTTP,特别是类JXOperation和JXURLConnectionOperation
https://stackoverflow.com/questions/14232908
复制相似问题