日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

NSOperation, NSOperationQueue 原理探析

發布時間:2023/11/29 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 NSOperation, NSOperationQueue 原理探析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

通過GNUstep的Foundation來嘗試探索下NSOperation,NSOperationQueue

?

示例程序

?

寫一個簡單的程序

?

- (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(@"已經取消");

????????????return;

????????}

????????NSLog(@"start");

????????[NSThread sleepForTimeInterval:1];

????}

}

?

實現的效果很簡單,打印三個strat,然后結束operation。

?

初探

?

根據閱讀GNU的源碼,也只能是猜想,但是嘗試了很多方法,沒有找到可以驗證的方案,但是實現原理上還是有很多相似處的。

?

NSOperation有三種狀態,isReady, isExecuting, isFinished.

?

很多其他的參數也會隨著NSOperationQueue的addOperation操作而變化著。

?

例如:

?

[self.operationQueue addOperation:operation];

?

添加一個未完成的NSOperation,其實就是將NSOperation添加到一個動態數組當中

?

- (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就是一個內部類,指代的就是NSOperationQueue,這里也是一個KVO的手動通知,進行operations,與operationCount的改變通知。

?

這里lock是NSRecursiveLock(遞歸鎖),原因我猜測是因為遞歸鎖的特性是可以被同一線程多次請求,而不會引起死鎖。同一線程的多次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];

}

?

其實在maxConcurrentOperationCount和suspended的setter方法里面都會調用_execute方法,以及在其他屬性如operationCount、operations、值發生變化的時候都會調用它。

?

那么_execute究竟是什么?

?

整個源碼都拿上來

?

- (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];

}

?

從源碼中可以看到,根據isConcurrent分為直接執行,和非直接執行,isConcurrent為YES的話可以直接執行start操作,但是如果isConcurrent為NO,那么這里使用detachNewThreadSelector來創建新的線程去執行start。

?

總結下來:

?

  • 所有的線程都很忙,并且沒有達到threadCount的最大值的時候會創建新的線程,這代表queue并不是一個線程,也有可能有幾個

  • _execute就是一個執行隊列,依次將等待隊列里面的所有operation進行start。

?

其實對于start函數來說的話,一個NSOperation并沒有新創建一條線程,依然操作在[NSThread currentThread]中,感興趣可以去做一下測試。從源碼中也是可以看出來的,

?

- (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];

}

?

總結

?

整個過程伴隨著很多屬性的變化,同步這些屬性,KVO在其中起著舉足輕重的作用,通過源碼也可以發現,NSOperationQueue對NSOperation的處理分為并發和非并發的情況。如果不想采用非并發的形式,我們可以直接自定義子類化,在NSOperationQueue中添加,并且管理就可以了,功能類似線程池的用法。

?

但是如果想要自定義的NSOperation是并發的僅僅是重寫isExecuting、isFinished、isConcurrent、isAsynchronous 這四個方法,isAsynchronous反回YES就可以了嗎?

?

從源碼中我們可以看到,NSOperation的start依然使用的[NSThread currentThread]。所以依然需要自己創建,例如:

?

[NSThread detachNewThreadSelector:@selector(start) toTarget:self withObject:nil];

?

現在來思考下,也就明白了為什么NSOperationQueue要有兩種處理方式了,如果NSOperation支持并發,然后NSOperationQueue在為其分配線程,那就是線程里面又跑了一條線程,這樣就很尷尬了,通過isConcurrent可以避免這種現象。

?

通常在大多數時候我們并不會直接去使用自定義的 NSOperation ,如果操作不復雜,可以直接使用 NSInvocationOperation 和 NSBlockOperation 這兩個子類。

?

如果真的需要使用多線程,通常都會用 NSOperationQueue來處理就可以了。

?

這里也是僅僅簡單的探索了一下,上面的源碼是封裝的NSThread,但是Apple的實現可能封裝的不是NSThread,因為斷點后并沒有看到跟NSThread相關的東西,還是有很多細節需要去推敲。

?

?

轉載于:https://www.cnblogs.com/fengmin/p/6108165.html

總結

以上是生活随笔為你收集整理的NSOperation, NSOperationQueue 原理探析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。