NSOperation, NSOperationQueue 原理探析

通过GNUstep的Foundation来尝试探索下NSOperation,NSOperationQueue

示例程序

写一个简单的程序

LDNSOperation为NSOperation的子类,重写strat方法

实现的效果很简单,打印三个strat,然后结束operation。

初探

根据阅读GNU的源码,也只能是猜想,但是尝试了很多方法,没有找到可以验证的方案,但是实现原理上还是有很多相似处的。

NSOperation有三种状态,isReady, isExecuting, isFinished.

很多其他的参数也会随着NSOperationQueue的addOperation操作而变化着。

例如:

添加一个未完成的NSOperation,其实就是将NSOperation添加到一个动态数组当中

internal就是一个内部类,指代的就是NSOperationQueue,这里也是一个KVO的手动通知,进行operations,与operationCount的改变通知。

这里lock是NSRecursiveLock(递归锁),原因我猜测是因为递归锁的特性是可以被同一线程多次请求,而不会引起死锁。同一线程的多次addOperation操做情况还是很多的。

每一次属性的变化,都伴随着其他属性的改变

其实在maxConcurrentOperationCount和suspended的setter方法里面都会调用_execute方法,以及在其他属性如operationCount、operations、值发生变化的时候都会调用它。

那么_execute究竟是什么?

整个源码都拿上来

从源码中可以看到,根据isConcurrent分为直接执行,和非直接执行,isConcurrent为YES的话可以直接执行start操作,但是如果isConcurrent为NO,那么这里使用detachNewThreadSelector来创建新的线程去执行start。

总结下来:

  • 所有的线程都很忙,并且没有达到threadCount的最大值的时候会创建新的线程,这代表queue并不是一个线程,也有可能有几个
  • _execute就是一个执行队列,依次将等待队列里面的所有operation进行start。

其实对于start函数来说的话,一个NSOperation并没有新创建一条线程,依然操作在[NSThread currentThread]中,感兴趣可以去做一下测试。从源码中也是可以看出来的,

总结

整个过程伴随着很多属性的变化,同步这些属性,KVO在其中起着举足轻重的作用,通过源码也可以发现,NSOperationQueue对NSOperation的处理分为并发和非并发的情况。如果不想采用非并发的形式,我们可以直接自定义子类化,在NSOperationQueue中添加,并且管理就可以了,功能类似线程池的用法。

但是如果想要自定义的NSOperation是并发的仅仅是重写isExecuting、isFinished、isConcurrent、isAsynchronous 这四个方法,isAsynchronous反回YES就可以了吗?

从源码中我们可以看到,NSOperation的start依然使用的[NSThread currentThread]。所以依然需要自己创建,例如:

现在来思考下,也就明白了为什么NSOperationQueue要有两种处理方式了,如果NSOperation支持并发,然后NSOperationQueue在为其分配线程,那就是线程里面又跑了一条线程,这样就很尴尬了,通过isConcurrent可以避免这种现象。

通常在大多数时候我们并不会直接去使用自定义的 NSOperation ,如果操作不复杂,可以直接使用 NSInvocationOperation 和 NSBlockOperation 这两个子类。

如果真的需要使用多线程,通常都会用 NSOperationQueue来处理就可以了。

这里也是仅仅简单的探索了一下,上面的源码是封装的NSThread,但是Apple的实现可能封装的不是NSThread,因为断点后并没有看到跟NSThread相关的东西,还是有很多细节需要去推敲。

最后

我相信GNU的Foundation与Apple的Foundation的实现还是有一些相似处的,以上就是一些对应的猜想,但是有一些地方还没有验证

例如:源码中看出一个队列里对于非并发操作最多允许8个线程,但是我自己试了试,貌似不是这个样子。可能是我的方法不对

打赏支持我写出更多好文章,谢谢!

打赏作者

打赏支持我写出更多好文章,谢谢!

任选一种支付方式

2 4 收藏 1 评论

关于作者:Lastdays

你知道白菜吗?我就是那个猪 个人主页 · 我的文章 · 3 ·   

相关文章

可能感兴趣的话题



直接登录
最新评论
  • bobo_bbb   2016/11/26

    现在AFN都是返回的NSURLSessionDataTask,怎么将这个任务A执行完毕再执行其他任务B,我添加了B依赖A,虽然是先执行A,在执行B, 但是A是异步请求,B要用到A的请求结果时,就有可能拿不到值,这个怎么确保A完全执行完了,再执行B?

跳到底部
返回顶部