NSOperation, NSOperationQueue 原理探析
通過(guò)GNUstep的Foundation來(lái)嘗試探索下NSOperation,NSOperationQueue
?
示例程序
?
寫一個(gè)簡(jiǎn)單的程序
?
- (void)viewDidLoad {
????[super viewDidLoad];
????// Do any additional setup after loading the view, typically from a nib.
????
????[self configurationQueue];
????LDNSOperation *operation = [[LDNSOperation alloc] init];
????[self.operationQueue addOperation:operation];
????[NSThread sleepForTimeInterval:3];
????[operation cancel];
????
}
?
-(void)configurationQueue{
????self.operationQueue = [[NSOperationQueue alloc] init];
????self.operationQueue.maxConcurrentOperationCount = 4;
}
?
LDNSOperation為NSOperation的子類,重寫strat方法
?
-(void)start{
????while (true) {
????????if(self.cancelled){
????????????NSLog(@"已經(jīng)取消");
????????????return;
????????}
????????NSLog(@"start");
????????[NSThread sleepForTimeInterval:1];
????}
}
?
實(shí)現(xiàn)的效果很簡(jiǎn)單,打印三個(gè)strat,然后結(jié)束operation。
?
初探
?
根據(jù)閱讀GNU的源碼,也只能是猜想,但是嘗試了很多方法,沒(méi)有找到可以驗(yàn)證的方案,但是實(shí)現(xiàn)原理上還是有很多相似處的。
?
NSOperation有三種狀態(tài),isReady, isExecuting, isFinished.
?
很多其他的參數(shù)也會(huì)隨著NSOperationQueue的addOperation操作而變化著。
?
例如:
?
[self.operationQueue addOperation:operation];
?
添加一個(gè)未完成的NSOperation,其實(shí)就是將NSOperation添加到一個(gè)動(dòng)態(tài)數(shù)組當(dāng)中
?
- (void) addOperation: (NSOperation *)op
{
??if (op == nil || NO == [op isKindOfClass: [NSOperation class]])
????{
??????[NSException raise: NSInvalidArgumentException
??format: @"[%@-%@] object is not an NSOperation",
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
????}
??[internal->lock lock];
??if (NSNotFound == [internal->operations indexOfObjectIdenticalTo: op]
????&& NO == [op isFinished])
????{
??????[op addObserver: self
?? forKeyPath: @"isReady"
??????options: NSKeyValueObservingOptionNew
??????context: NULL];
??????[self willChangeValueForKey: @"operations"];
??????[self willChangeValueForKey: @"operationCount"];
??????[internal->operations addObject: op];
??????[self didChangeValueForKey: @"operationCount"];
??????[self didChangeValueForKey: @"operations"];
??????if (YES == [op isReady])
??????{
??????????[self observeValueForKeyPath: @"isReady"
??????ofObject: op
change: nil
?????? context: nil];
??????}
????}
??[internal->lock unlock];
}
?
internal就是一個(gè)內(nèi)部類,指代的就是NSOperationQueue,這里也是一個(gè)KVO的手動(dòng)通知,進(jìn)行operations,與operationCount的改變通知。
?
這里lock是NSRecursiveLock(遞歸鎖),原因我猜測(cè)是因?yàn)檫f歸鎖的特性是可以被同一線程多次請(qǐng)求,而不會(huì)引起死鎖。同一線程的多次addOperation操做情況還是很多的。
?
每一次屬性的變化,都伴隨著其他屬性的改變
?
- (void) observeValueForKeyPath: (NSString *)keyPath
?????? ofObject: (id)object
???????????????????????? change: (NSDictionary *)change
????????????????????????context: (void *)context
{
??[internal->lock lock];
??if (YES == [object isFinished])
????{
??????internal->executing--;
??????[object removeObserver: self
??forKeyPath: @"isFinished"];
??????[internal->lock unlock];
??????[self willChangeValueForKey: @"operations"];
??????[self willChangeValueForKey: @"operationCount"];
??????[internal->lock lock];
??????[internal->operations removeObjectIdenticalTo: object];
??????[internal->lock unlock];
??????[self didChangeValueForKey: @"operationCount"];
??????[self didChangeValueForKey: @"operations"];
????}
??else if (YES == [object isReady])
????{
??????[object removeObserver: self
??forKeyPath: @"isReady"];
??????[internal->waiting addObject: object];
??????[internal->lock unlock];
????}
??[self _execute];
}
?
其實(shí)在maxConcurrentOperationCount和suspended的setter方法里面都會(huì)調(diào)用_execute方法,以及在其他屬性如operationCount、operations、值發(fā)生變化的時(shí)候都會(huì)調(diào)用它。
?
那么_execute究竟是什么?
?
整個(gè)源碼都拿上來(lái)
?
- (void) _execute
{
??NSInteger max;
?
??[internal->lock lock];
?
??max = [self maxConcurrentOperationCount];
??if (NSOperationQueueDefaultMaxConcurrentOperationCount == max)
????{
??????max = maxConcurrent;
????}
?
??while (NO == [self isSuspended]
????&& max > internal->executing
????&& [internal->waiting count] > 0)
????{
??????NSOperation *op;
??????op = [internal->waiting objectAtIndex: 0];
??????[internal->waiting removeObjectAtIndex: 0];
??????[op addObserver: self
?? forKeyPath: @"isFinished"
??????options: NSKeyValueObservingOptionNew
??????context: NULL];
??????internal->executing++;
??????if (YES == [op isConcurrent])
{
??????????[op start];
}
??????else
{
??NSUInteger pending;
?
??[internal->cond lock];
??pending = [internal->starting count];
??[internal->starting addObject: op];
??if (0 == internal->threadCount
????|| (pending > 0 && internal->threadCount
????{
??????internal->threadCount++;
??????[NSThread detachNewThreadSelector: @selector(_thread)
?????? toTarget: self
???? withObject: nil];
????}
??/* Tell the thread pool that there is an operation to start.
?? */
??[internal->cond unlockWithCondition: 1];
}
????}
??[internal->lock unlock];
}
?
從源碼中可以看到,根據(jù)isConcurrent分為直接執(zhí)行,和非直接執(zhí)行,isConcurrent為YES的話可以直接執(zhí)行start操作,但是如果isConcurrent為NO,那么這里使用detachNewThreadSelector來(lái)創(chuàng)建新的線程去執(zhí)行start。
?
總結(jié)下來(lái):
?
-
所有的線程都很忙,并且沒(méi)有達(dá)到threadCount的最大值的時(shí)候會(huì)創(chuàng)建新的線程,這代表queue并不是一個(gè)線程,也有可能有幾個(gè)
-
_execute就是一個(gè)執(zhí)行隊(duì)列,依次將等待隊(duì)列里面的所有operation進(jìn)行start。
?
其實(shí)對(duì)于start函數(shù)來(lái)說(shuō)的話,一個(gè)NSOperation并沒(méi)有新創(chuàng)建一條線程,依然操作在[NSThread currentThread]中,感興趣可以去做一下測(cè)試。從源碼中也是可以看出來(lái)的,
?
- (void) start
{
??NSAutoreleasePool *pool = [NSAutoreleasePool new];
??double prio = [NSThread??threadPriority];
?
??[internal->lock lock];
??NS_DURING
????{
??????if (YES == [self isConcurrent])
{
??[NSException raise: NSInvalidArgumentException
??????format: @"[%@-%@] called on concurrent operation",
????NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
??????if (YES == [self isExecuting])
{
??[NSException raise: NSInvalidArgumentException
??????format: @"[%@-%@] called on executing operation",
????NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
??????if (YES == [self isFinished])
{
??[NSException raise: NSInvalidArgumentException
??????format: @"[%@-%@] called on finished operation",
????NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
??????if (NO == [self isReady])
{
??[NSException raise: NSInvalidArgumentException
??????format: @"[%@-%@] called on operation which is not ready",
????NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
??????if (NO == internal->executing)
{
??[self willChangeValueForKey: @"isExecuting"];
??internal->executing = YES;
??[self didChangeValueForKey: @"isExecuting"];
}
????}
??NS_HANDLER
????{
??????[internal->lock unlock];
??????[localException raise];
????}
??NS_ENDHANDLER
??[internal->lock unlock];
?
??NS_DURING
????{
??????if (NO == [self isCancelled])
{
??[NSThread setThreadPriority: internal->threadPriority];
??[self main];
}
????}
??NS_HANDLER
????{
??????[NSThread setThreadPriority:??prio];
??????[localException raise];
????}
??NS_ENDHANDLER;
?
??[self _finish];
??[pool release];
}
?
總結(jié)
?
整個(gè)過(guò)程伴隨著很多屬性的變化,同步這些屬性,KVO在其中起著舉足輕重的作用,通過(guò)源碼也可以發(fā)現(xiàn),NSOperationQueue對(duì)NSOperation的處理分為并發(fā)和非并發(fā)的情況。如果不想采用非并發(fā)的形式,我們可以直接自定義子類化,在NSOperationQueue中添加,并且管理就可以了,功能類似線程池的用法。
?
但是如果想要自定義的NSOperation是并發(fā)的僅僅是重寫isExecuting、isFinished、isConcurrent、isAsynchronous 這四個(gè)方法,isAsynchronous反回YES就可以了嗎?
?
從源碼中我們可以看到,NSOperation的start依然使用的[NSThread currentThread]。所以依然需要自己創(chuàng)建,例如:
?
[NSThread detachNewThreadSelector:@selector(start) toTarget:self withObject:nil];
?
現(xiàn)在來(lái)思考下,也就明白了為什么NSOperationQueue要有兩種處理方式了,如果NSOperation支持并發(fā),然后NSOperationQueue在為其分配線程,那就是線程里面又跑了一條線程,這樣就很尷尬了,通過(guò)isConcurrent可以避免這種現(xiàn)象。
?
通常在大多數(shù)時(shí)候我們并不會(huì)直接去使用自定義的 NSOperation ,如果操作不復(fù)雜,可以直接使用 NSInvocationOperation 和 NSBlockOperation 這兩個(gè)子類。
?
如果真的需要使用多線程,通常都會(huì)用 NSOperationQueue來(lái)處理就可以了。
?
這里也是僅僅簡(jiǎn)單的探索了一下,上面的源碼是封裝的NSThread,但是Apple的實(shí)現(xiàn)可能封裝的不是NSThread,因?yàn)閿帱c(diǎn)后并沒(méi)有看到跟NSThread相關(guān)的東西,還是有很多細(xì)節(jié)需要去推敲。
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/fengmin/p/6108165.html
總結(jié)
以上是生活随笔為你收集整理的NSOperation, NSOperationQueue 原理探析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 梦到别人找工作是什么意思
- 下一篇: mysql语句添加索引