文章優(yōu)先發(fā)布于小小廚師的廚房
NSOperation,NSOperationQueue
demo洗贰。最快熟悉的方式就是自己碼一遍。
NSOperation
NSOperation是一個抽象類,我們可以直接使用其子類NSInvocationOperation
和NSBlockOperation
蔬胯,或者封裝NSOperation
子類來添加要在線程中執(zhí)行的操作芥炭。默認情況下NSOperation單獨使用時執(zhí)行同步操作。
NSInvocationOperation
操作在當前線程中執(zhí)行愿伴,不開啟新線程。
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(p_task1) object:nil];
[op1 start];
NSBlockOperation
- 如果封裝多個操作电湘,會自動開啟線程隔节,線程數(shù)由系統(tǒng)決定。封裝多個操作時不受串行隊列控制寂呛。
創(chuàng)建操作:
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"--->blockOp1-Thread:%@",[NSThread currentThread]);
}];
[op start];
添加操作:通過- (void)addExecutionBlock:(void (^)(void))block;
為NSBlockOperation
添加額外的操作官帘。這些操作可以在不同的線程中并發(fā)執(zhí)行,線程的切換由系統(tǒng)決定昧谊。如果添加的操作較多刽虹,blockOperationWithBlock:
中的操作可能會在其他線程中執(zhí)行。
自定義NSOperation子類
通過重寫main
或start
方法來定義自己的NSOperation
對象呢诬。
// 當前線程執(zhí)行
@implementation CustomOperation
- (void)main {
if (!self.cancelled) {
NSLog(@"--->customMainOp-Thread:%@",[NSThread currentThread]);
}
}
@end
并發(fā)執(zhí)行
需要重寫的方法:
必須:
-
start
:是一個operation的起點涌哲。重寫時不要調(diào)用父類的方法。 -
isExecuting
尚镰、isFinished
:operation狀態(tài)阀圾。當值發(fā)生變化時需要生成相應的KVO
通知,以便外界能夠觀察狀態(tài)變化狗唉。 -
isAsynchronous
:返回是否并發(fā)初烘。
可選:
-
main
:實現(xiàn)改operation相關(guān)聯(lián)的任務。用main
方法實現(xiàn)任務可使 設(shè)置代碼 任務代碼 得到分離,從而使operation的結(jié)構(gòu)更加清晰肾筐。
@implementation ConcurrentOperation
@synthesize executing = _executing;
@synthesize finished = _finished;
- (BOOL)isExecuting {
return _executing;
}
- (BOOL)isFinished {
return _finished;
}
- (BOOL)isAsynchronous {
return YES;
}
- (void)start {
if (self.cancelled) {
[self willChangeValueForKey:@"isFinished"];
_finished = YES;
[self willChangeValueForKey:@"isFinished"];
return;
}
[self willChangeValueForKey:@"isExecuting"];
[NSThread detachNewThreadSelector:@selector(main) toTarget:self withObject:nil];
_executing = YES;
[self willChangeValueForKey:@"isExecuting"];
}
- (void)main {
NSLog(@"--->concurrentOp-Thread:%@",[NSThread currentThread]);
[self willChangeValueForKey:@"isExecuting"];
_executing = NO;
[self didChangeValueForKey:@"isExecuting"];
[self willChangeValueForKey:@"isFinished"];
_finished = YES;
[self didChangeValueForKey:@"isFinished"];
}
@end
- 注意:當operation被cancel時哆料,仍然需要手動出發(fā)
isFinished
的KVO
通知。因為當一個Operation依賴其他Operation時吗铐,它會觀察所有其他Operation的isFinished
的值的變化东亦,只有當它依賴的所有Operation的isFinished
的值為YES時,這個operation才能開始執(zhí)行唬渗。
NSOperationQueue
- NSOperation配合NSOperationQueue來實現(xiàn)多線程典阵。
NSOperationQueue
對于添加到隊列中的操作,首先進入準備就緒
的狀態(tài)镊逝,然后進入就緒狀態(tài)
壮啊,就緒狀態(tài)
的操作的開始執(zhí)行順序由操作之間相對的優(yōu)先級決定。 - 理論上我們可以創(chuàng)建任意數(shù)量的OperationQueue撑蒜,但數(shù)量越多并不意味著我們就能同時執(zhí)行越多的Operation歹啼。因為并發(fā)的Operation數(shù)量由系統(tǒng)決定,系統(tǒng)會根據(jù)自身動態(tài)調(diào)整减江。
- 操作隊列通過設(shè)置
maxConcurrentOperationCount
控制并發(fā)、串行捻爷,默認值為-1辈灼,并發(fā)執(zhí)行,線程數(shù)由系統(tǒng)決定也榄。為1時為串行隊列巡莹。
隊列類型:
- 主隊列:
[NSOperationQueue mainQueue];
- 異步主隊列:
[[NSOperationQueue alloc] init];
操作加入到隊列
addOperation
NSOperationQueue *customQueue = [[NSOperationQueue alloc] init];
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(p_task1) object:nil];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
sleep(2);
NSLog(@"--->OpQueue1-Thread:%@",[NSThread currentThread]);
}];
[customQueue addOperation:op1];
[customQueue addOperation:op3];
addOperationWithBlock
NSOperationQueue *customQueue = [[NSOperationQueue alloc] init];
[customQueue addOperationWithBlock:^{
NSLog(@"--->BlockOpQueue1-Thread:%@",[NSThread currentThread]);
}];
并發(fā)控制
operationQueue的并發(fā)控制由maxConcurrentOperationCount
來控制。
- 默認值
NSOperationQueueDefaultMaxConcurrentOperationCount
甜紫,-1,系統(tǒng)控制并發(fā)數(shù)量降宅。 - 設(shè)置為1時,串行隊列囚霸,操作依次執(zhí)行腰根,但操作處于的線程由系統(tǒng)控制。
-
>1
時拓型,最大并發(fā)數(shù)不能大于系統(tǒng)能提供的并發(fā)數(shù)额嘿。
NSOperation操作依賴
NSOperation依賴關(guān)系由自身管理,與Queue無關(guān)。
添加是在Operation操作執(zhí)行之前添加劣挫。
防止循環(huán)依賴
- 添加依賴:
- (void)addDependency:(NSOperation *)op;
- 移除依賴:
- (void)removeDependency:(NSOperation *)op;
NSOperation優(yōu)先級 - 針對已加入到到隊列中的操作有效册养。
- operation的執(zhí)行順序的第一要素是它們的
isReady
狀態(tài),其次是它們在隊列中的優(yōu)先級压固。優(yōu)先級只決定isReady
為YES的Operation的執(zhí)行順序球拦。當operation依賴未執(zhí)行完時,isReady
為NO。 - 隊列優(yōu)先級只應用于相同的
Queue
中的Operation
之間坎炼。 - Operation依賴 > Operation優(yōu)先級愧膀。
- 默認優(yōu)先級:
NSOperationQueuePriorityNormal
。
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
NSOperationQueuePriorityVeryLow = -8L,
NSOperationQueuePriorityLow = -4L,
NSOperationQueuePriorityNormal = 0,
NSOperationQueuePriorityHigh = 4,
NSOperationQueuePriorityVeryHigh = 8
};
線程間通信
NSOperationQueue *opQ = [[NSOperationQueue alloc] init];
[opQ addOperationWithBlock:^{
sleep(1);
NSLog(@"--->1-Thread:%@",[NSThread currentThread]);
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"--->2-Thread:%@",[NSThread currentThread]);
}];
}];
線程安全
若每個線程中對全局變量点弯、靜態(tài)變量只有讀操作扇调,而無寫操作,一般來說抢肛,這個全局變量是線程安全的狼钮;若有多個線程同時執(zhí)行寫操作(更改變量),一般都需要考慮線程同步捡絮,否則的話就可能影響線程安全熬芜。
線程安全解決:加鎖
- @synchronized
- NSLock、NSRecrusiveLock福稳、NSCondition涎拉、NSConditionLock
- pthread_mutex
- dispatch_semaphore
- atomic
性能對比:
參考
https://bujige.net/blog/iOS-Complete-learning-NSOperation.html
http://blog.leichunfeng.com/blog/2015/07/29/ios-concurrency-programming-operation-queues/
https://blog.ibireme.com/2016/01/16/spinlock_is_unsafe_in_ios/