NSOperation
、NSOperationQueue
是蘋果提供的一套多線程解決方案,是基於GCD
更高一層的封裝,徹底面向對象。但比 GCD
更簡單易用、代碼可讀性更高。數組
- 好處:
可添加完成的代碼塊complete
,在操做完成後執行。 添加操做之間的依賴關係,方便控制執行順序。 設定操做執行的優先級。 能夠取消一個操做的執行。 使用 KVO 觀察執行狀態:isExecuteing、isFinished、isCancelled
。安全
在 NSOperation、NSOperationQueue
中也有相似的「任務」和「隊列」的概念。markdown
任務,或操做。即,你在線程中執行的那段代碼。NSOperation
是一個抽象類,要使用其子類 NSInvocationOperation、NSBlockOperation
,或者自定義子類來封裝任務。多線程
指用來存放操做的隊列。併發
主隊列、自定義隊列。 主隊列運行在主線程之上,而自定義隊列在後臺執行。異步
不一樣於 GCD 中的隊列 FIFO(先進先出)的原則。對於添加到NSOperationQueue
中的任務,首先進入準備就緒的狀態(就緒狀態取決於任務之間的依賴關係),而後進入就緒狀態的任務的開始執行順序(而非執行完成的順序)由任務之間相對的優先級決定。性能
任務隊列經過設置最大併發操做數maxConcurrentOperationCount
來控制併發、串行。atom
默認狀況下,NSOperation
單獨使用時,系統同步執行操做。只有配合 NSOperationQueue
才能更好地實現異步執行。spa
建立任務:先將須要執行的任務,封裝到一個 NSOperation
對象中。 建立隊列:即NSOperationQueue
對象。 將任務加入到隊列中:將NSOperation
對象添加到 NSOperationQueue
對象中。線程
系統就會自動將NSOperationQueue
中的 NSOperation
取出來,在新線程中執行操做。
NSOperation
是個抽象類,不能直接用來封裝任務。要使用它的子類來封裝任務:NSInvocationOperation、NSBlockOperation
以及自定義的子類。
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task1) object:nil];
[op start]; // 開始執行
複製代碼
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
// do some thing...
}];
[op start];
複製代碼
此外,能夠爲任務對象添加額外任務。
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
// do something...
}];
// 添加額外的操做
[op addExecutionBlock:^{
// do otherthing...
}];
複製代碼
- 在沒有使用
NSOperationQueue
、在主線程中單獨使用NSOperation
的子類對象 執行一個任務的狀況下,任務是在當前線程執行的,並無開啓新線程。- 使用子類
NSBlockOperation
,並調用方法addExecutionBlock:
的狀況下,若是添加的操做的個數多,就會自動開啓新線程。
有兩種:主隊列、自定義隊列。
NSOperationQueue *queue = [NSOperationQueue mainQueue];
複製代碼
凡是添加到主隊列中的操做,都會放到主線程中執行。
添加到這種隊列中的操做,就會自動放到子線程中執行;同時包含串行、併發功能。
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue. maxConcurrentOperationCount = 3;
複製代碼
可設置屬性maxConcurrentOperationCount
最大併發操做數,決定串行、併發。
屬性
maxConcurrentOperationCount
= 1; // 串行隊列,任務只能按順序串行執行的 = 2; // 併發隊列,>2時,任務是併發執行的,可同時執行多個操做 = 8; // 併發隊列 固然這個值不該超過系統限制,即便設置了一個很大的值,系統也會自動調整爲 min。
方法addOperation:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task1) object:nil];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
//.....
}];
[op3 addExecutionBlock:^{
//.....
}];
[queue addOperation:op1];
[queue addOperation:op3];
複製代碼
方法addOperationWithBlock:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 添加操做到隊列中
[queue addOperationWithBlock:^{
//.....
}];
複製代碼
操做和操做隊列、使用步驟和基本使用方法、控制串行/併發執行、
經過依賴,能夠很方便的控制任務之間的執行前後順序。
addDependency:(NSOperation *)op;
添加依賴
removeDependency:(NSOperation *)op;
移除依賴 NSArray<NSOperation *> *dependencies;
在當前任務開始執行以前,已完成任務對象的數組
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
//.....
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
//.....
}];
// 添加依賴
[op2 addDependency:op1]; //讓op2 依賴於 op1。表示先執行op1
[queue addOperation:op1];
[queue addOperation:op2];
複製代碼
當一個任務的全部依賴都已經完成時,任務對象一般會進入準備就緒狀態,等待執行。
###NSOperation 優先級 NSOperation
提供了優先級屬性queuePriority
。默認狀況下,全部新建立的操做對象優先級都是NSOperationQueuePriorityNormal
。但能夠經過setQueuePriority:
方法來改變在同一隊列中的任務的優先級。
queuePriority
屬性適用於同一隊列中的任務,不一樣隊列的任務不適用。
優先級的取值: NSOperationQueuePriorityVeryLow = -8L,
NSOperationQueuePriorityLow = -4L,
NSOperationQueuePriorityNormal = 0,
(默認) NSOperationQueuePriorityHigh = 4,
NSOperationQueuePriorityVeryHigh = 8
queuePriority
決定了進入準備就緒狀態下的任務之間的開始執行順序
。而且,優先級不能取代依賴關係。若是,一個隊列中既包含了準備就緒狀態的任務A,又包含了未準備就緒的任務B,B的優先級比A高。那麼,會優先執行B。若是要控制操做間的啓動順序,則必須使用依賴關係。
處理完子行程的任務,返回主行程處理
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[queue addOperationWithBlock:^{
// .....
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// 回到主線程, 進行一些 UI 刷新等操做
}];
}];
複製代碼
正在執行的操做,沒法取消。只能取消未執行的操做。
1.可經過 cancel 方法,取消未執行的單個操做。
NSOperationQueue *queue1 = [[NSOperationQueue alloc]init];
NSBlockOperation *block1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"block11");
}];
NSBlockOperation *block2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"block22");
}];
NSBlockOperation *block3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"block33");
}];
[block3 cancel];
[queue1 addOperations:@[block1,block2,block3] waitUntilFinished:YES];
複製代碼
// 移除隊列裏面全部的操做
[queue1 cancelAllOperations];
複製代碼
3.掛起隊列,使隊列任務再也不執行,但正在執行的操做沒法掛起。
queue1.suspended = YES;
複製代碼
要實現取消正在執行的操做,能夠自定義NSOperation,其實就是攔截main方法。
main方法: 1.任何操做在執行時,首先會調用start方法,它會更新操做的狀態過濾操做(如過濾掉處於「取消」狀態的操做) 2.經start方法過濾後,只有正常可執行的操做,就會調用main方法。 3.重寫操做的入口方法(main),就能夠在這個方法裏面指定操做執行的任務。 4.main方法默認是在子線程異步執行的。
有多個線程可能會同時運行一段代碼。若是每次運行結果和單線程運行的結果同樣,且其餘變量的值也和預期的是同樣的,就是線程安全的。 若每一個線程中對全局變量、靜態變量只有讀操做(無寫操做),通常這個變量是線程安全的。 如有多個線程同時執行寫操做(更改變量的值),就須要考慮線程同步,不然就可能影響線程安全。
可理解爲線程 A 和 線程 B 一塊配合,A 執行到必定程度時要依靠線程 B 的某個結果,因而停下來,示意 B 運行;B 依言執行,再將結果給 A;A 再繼續操做。 例子:火車票售賣
能夠給線程加鎖,在一個線程執行該操做的時候,不容許其餘線程進行操做。
iOS 實現線程加鎖有不少種方式:
dispatch_semaphore
:建議使用,性能較好 NSLock
@synchronized
:性能最差 os_unfair_lock
:iOS10開始 NSCondition
NSConditionLock
pthread_mute
OSSpinLock
:iOS10 廢棄 atomic(property) set/get
:原子操做
思想:執行寫操做前,上鎖,寫操做完,解鎖。
自旋鎖:
會忙等。所謂忙等,即在訪問被鎖資源時,調用者線程不會休眠,而是不停循環在那裏,直到被鎖資源釋放鎖。 適用的狀況 預計線程等待鎖的時間很短 加鎖的代碼(臨界區)常常被調用,但競爭狀況不多發生 CPU資源不緊張、多核處理器
互斥鎖:
會休眠。 所謂休眠,即在訪問被鎖資源時,調用者線程會休眠,此時cpu能夠調度其餘線程工做。直到被鎖資源釋放鎖。此時會喚醒休眠線程。 適用的狀況 預計線程等待鎖的時間較長 臨界區競爭很是激烈 臨界區有IO操做 臨界區代碼複雜或者循環量大 單核處理器
死鎖:
一般是指2個操做相互等待對方完成,形成死循環,因而2個操做都沒法進行,就產生了死鎖。
.
建議在複雜項目中 使用。
建議在簡單項目中 使用。
1.NSOperationQueue能夠輕鬆在Operation間設置依賴關係,而GCD 須要寫不少代碼才能實現。 2. NSOperation能夠很方便地調整執⾏順序、設置最⼤併發數。 3.NSOperationQueue⽀支持KVO,能夠監測operation的狀態:是否正在執行 (isExecuted)
、 是否結束(isFinished)
,是否取消(isCanceld)
4.GCD只⽀支持FIFO的隊列列