iOS多線程-NSOperation

簡介

NSOperationNSOperationQueue 是蘋果提供的一套多線程解決方案,是基於GCD更高一層的封裝,徹底面向對象。但比 GCD 更簡單易用、代碼可讀性更高。數組

  • 好處:

可添加完成的代碼塊complete,在操做完成後執行。 添加操做之間的依賴關係,方便控制執行順序。 設定操做執行的優先級。 能夠取消一個操做的執行。 使用 KVO 觀察執行狀態:isExecuteing、isFinished、isCancelled安全

NSOperation、NSOperationQueue 中也有相似的「任務」和「隊列」的概念。markdown

1.NSOperation

任務,或操做。即,你在線程中執行的那段代碼。NSOperation是一個抽象類,要使用其子類 NSInvocationOperation、NSBlockOperation,或者自定義子類來封裝任務。多線程

2.NSOperationQueue

指用來存放操做的隊列。併發

  • 隊列類型

主隊列、自定義隊列。 主隊列運行在主線程之上,而自定義隊列在後臺執行。異步

  • 任務狀態和順序

不一樣於 GCD 中的隊列 FIFO(先進先出)的原則。對於添加到NSOperationQueue 中的任務,首先進入準備就緒的狀態(就緒狀態取決於任務之間的依賴關係),而後進入就緒狀態的任務的開始執行順序(而非執行完成的順序)由任務之間相對的優先級決定。性能

  • 併發和串行

任務隊列經過設置最大併發操做數maxConcurrentOperationCount來控制併發、串行。atom

3. NSOperation實現多線程

默認狀況下,NSOperation 單獨使用時,系統同步執行操做。只有配合 NSOperationQueue 才能更好地實現異步執行。spa

  • 使用步驟分爲3步

建立任務:先將須要執行的任務,封裝到一個 NSOperation 對象中。 建立隊列:即NSOperationQueue 對象。 將任務加入到隊列中:將NSOperation對象添加到 NSOperationQueue 對象中。線程

系統就會自動將NSOperationQueue中的 NSOperation 取出來,在新線程中執行操做。

3.1建立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: 的狀況下,若是添加的操做的個數多,就會自動開啓新線程。
2.建立NSOperationQueue隊列

有兩種:主隊列、自定義隊列。

  • 主隊列
NSOperationQueue *queue = [NSOperationQueue mainQueue];
複製代碼

凡是添加到主隊列中的操做,都會放到主線程中執行。

  • 自定義隊列

添加到這種隊列中的操做,就會自動放到子線程中執行;同時包含串行、併發功能。

NSOperationQueue *queue = [[NSOperationQueue alloc] init];

queue. maxConcurrentOperationCount = 3;
複製代碼

可設置屬性maxConcurrentOperationCount最大併發操做數,決定串行、併發。

屬性maxConcurrentOperationCount

= 1; // 串行隊列,任務只能按順序串行執行的 = 2; // 併發隊列,>2時,任務是併發執行的,可同時執行多個操做 = 8; // 併發隊列 固然這個值不該超過系統限制,即便設置了一個很大的值,系統也會自動調整爲 min。

3.將操做加入到隊列中

方法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:^{
        //.....
    }];
複製代碼

操做和操做隊列、使用步驟和基本使用方法、控制串行/併發執行、

  • 把任務添加到隊列中,至關於調用了任務的start方法

NSOperation 依賴

經過依賴,能夠很方便的控制任務之間的執行前後順序。

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];
複製代碼
  1. cancelAllOperations方法,但沒法移除 正在執行的操做。
// 移除隊列裏面全部的操做
[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個操做都沒法進行,就產生了死鎖。

經常使用屬性和方法概括

.

NSOperation 和GCD的對比

  • NSOperation 是對線程的高度抽象,在項目中使 用它,會使項目的程序結構更好。子類化 NSOperation 的設計思路,是具備面向對 象的優勢(複用、封裝),使得實現是多線程支持,而接口簡單。

建議在複雜項目中 使用。

  • GCD 自己很是簡單、易用,對於不復雜的多線程操 做,會節省代碼量,而 Block 參數的使用,會是代碼更爲易讀。

建議在簡單項目中 使用。

  • NSOperation的優勢

1.NSOperationQueue能夠輕鬆在Operation間設置依賴關係,而GCD 須要寫不少代碼才能實現。 2. NSOperation能夠很方便地調整執⾏順序、設置最⼤併發數。 3.NSOperationQueue⽀支持KVO,能夠監測operation的狀態:是否正在執行 (isExecuted)、 是否結束(isFinished),是否取消(isCanceld) 4.GCD只⽀支持FIFO的隊列列

相關文章
相關標籤/搜索