iOS中的多執行緒(關於NSOperationQueue)

語言: CN / TW / HK

highlight: a11y-dark theme: cyanosis


「這是我參與11月更文挑戰的第12天,活動詳情檢視:2021最後一次更文挑戰

關於NSOperationQueue

NSOperationQueue 一共有兩種佇列: - 主佇列

```js
//獲取主佇列 
NSOperationQueue *queue = [NSOperationQueue mainQueue];
```
  • 自定義佇列:通過設定最大併發數屬性來控制任務是併發執行還是序列執行
    • 併發執行
    • 序列執行 js //獲取自定義佇列 NSOperationQueue *queue = [[NSOperationQueue alloc]init];

NSInvocationOperation 和 NSOperationQueue 組合

示例: ```js //建立佇列(建立的佇列中的任務預設是非同步執行的) NSOperationQueue *queue = [[NSOperationQueue alloc]init];

//建立任務 NSInvocationOperation op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run1) object:nil]; NSInvocationOperation op2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run2) object:nil];

//任務新增佇列 [queue addOperation:op1]; [queue addOperation:op2];

  • (void)run1{     NSLog(@"1-- %@",[NSThread currentThread]); }
  • (void)run2{     NSLog(@"2-- %@",[NSThread currentThread]); } ``` log:

Snip20211109_11.png


NSInvocationOperation 和 NSBlockOperation 組合

示例: ```js //建立佇列(建立的佇列中的任務預設是非同步執行的) NSOperationQueue *queue = [[NSOperationQueue alloc]init];

//建立任務 NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"1-- %@",[NSThread currentThread]); }];

NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"2-- %@",[NSThread currentThread]); }];

//新增額外任務 [op1 addExecutionBlock:^{ NSLog(@"3-- %@",[NSThread currentThread]); }];

//新增額外任務 [op1 addExecutionBlock:^{ NSLog(@"4-- %@",[NSThread currentThread]); }];

//任務新增佇列 [queue addOperation:op1]; [queue addOperation:op2];

//可以直接建立任務到佇列中去 [queue addOperationWithBlock:^{ NSLog(@"5-- %@",[NSThread currentThread]); }]; ``` log:(任務都是併發執行的) Snip20211109_13.png


NSInvocationOperation 和 自定義NSOperation 組合

示例: ```js //建立佇列(建立的佇列中的任務預設是非同步執行的) NSOperationQueue *queue = [[NSOperationQueue alloc]init];

//建立任務需要繼承NSOperation,執行的操作需要放在這個自定義類的main中 Operation *op1 = [[Operation alloc]init];

//建立任務需要繼承NSOperation,執行的操作需要放在這個自定義類的main中 Operation *op2 = [[Operation alloc]init];

//任務新增佇列 [queue addOperation:op1]; [queue addOperation:op2]; ``` log: Snip20211109_14.png


NSOperation其它用法

設定最大併發數(控制任務併發和序列)

  • 最大併發數:佇列在同一時間中最多有多少個任務可以執行
  • 通過屬性 maxConcurrentOperationCount 設定最大併發數量
    • maxConcurrentOperationCount = -1,不進行限制,併發執行
    • maxConcurrentOperationCount = 1,序列佇列,序列執行
    • maxConcurrentOperationCount > 1,併發佇列,併發執行
    • maxConcurrentOperationCount = 0,不會執行

注:
- 同一時間最多有多少個任務可以執行 - 序列執行並不是只開一條執行緒,它只是執行緒同步,區別序列還是並行,不是看它開了多少條執行緒,而是看任務的執行方式,是有序的還是無序的

示例:(設定最大併發數為1,佇列為序列執行任務) ```js NSOperationQueue *queue = [[NSOperationQueue alloc]init];

//最大併發數設定為1 queue.maxConcurrentOperationCount  = 1;

[queue addOperationWithBlock:^{ [NSThread sleepForTimeInterval:2];     NSLog(@"1-- %@",[NSThread currentThread]); }]; [queue addOperationWithBlock:^{ [NSThread sleepForTimeInterval:2];     NSLog(@"2-- %@",[NSThread currentThread]); }]; [queue addOperationWithBlock:^{ [NSThread sleepForTimeInterval:2];     NSLog(@"3-- %@",[NSThread currentThread]); }]; ``` log:(執行緒為多條,但是屬於執行緒同步) Snip20211110_20.png

示例:(設定最大併發數為2,佇列為序列執行任務)

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

//最大併發數設定為1 queue.maxConcurrentOperationCount  = 2;

[queue addOperationWithBlock:^{ [NSThread sleepForTimeInterval:2];     NSLog(@"1-- %@",[NSThread currentThread]); }]; [queue addOperationWithBlock:^{ [NSThread sleepForTimeInterval:2];     NSLog(@"2-- %@",[NSThread currentThread]); }]; [queue addOperationWithBlock:^{ [NSThread sleepForTimeInterval:2];     NSLog(@"3-- %@",[NSThread currentThread]); }]; ``` log:(執行緒為多條,非同步執行,不管加入佇列有多少操作,實際佇列併發數為2) Snip20211110_21.png

佇列的暫停和恢復以及取消

  • 暫停和恢復佇列

    • 暫停表示不繼續執行佇列中的下一個任務,暫停是可以恢復的
    • 佇列中的任務也是有狀態的,分為:已經執行完畢、正在執行、排隊等待狀態
    • 不能暫停當前正在處於執行狀態的任務 js //暫停 queue.suspended = YES; //恢復 queue.suspended = NO;
  • 取消佇列的所有操作

    • 取消佇列裡所有的操作
    • 取消之後,當前正在執行的操作的下一個操作將不再執行
    • 取消操作是不可恢復的
    • 已經取消操作,再次啟動需要重新加入佇列 js [queue cancelAllOperations] 示例:

建立佇列並執行 ```js - (void)touchesBegan:(NSSet )touches withEvent:(UIEvent )event{ self.queue = [[NSOperationQueue alloc]init]; self.queue.maxConcurrentOperationCount = 1;

[self.queue addOperationWithBlock:^{
    [NSThread sleepForTimeInterval:1.0];
    NSLog(@"-1--%@",[NSThread currentThread]);
}];

[self.queue addOperationWithBlock:^{
    [NSThread sleepForTimeInterval:1.0];
    NSLog(@"-2--%@",[NSThread currentThread]);
}];

[self.queue addOperationWithBlock:^{
    [NSThread sleepForTimeInterval:1.0];
    NSLog(@"-3--%@",[NSThread currentThread]);
}];

[self.queue addOperationWithBlock:^{
    [NSThread sleepForTimeInterval:1.0];
    NSLog(@"-4--%@",[NSThread currentThread]);
}];

}

//暫停和恢復佇列 - (void)suspendedClick:(id)sender {     if(self.queue.suspended){         //恢復佇列,繼續執行         self.queue.suspended  = NO;     }else{         //掛起(暫停佇列)         self.queue.suspended  = YES;     } }

//取消佇列 - (void)cancelClick:(id)sender {     [self.queue cancelAllOperations]; } ```

設定佇列操作依賴

  • NSOperation 之間可以設定依賴來保證執行順序,比如A執行完之後再執行B,就可以使用設定依賴
  • 設定依賴 js [op1 addDependency:op3];
  • 取消依賴 js [op1 removeDependency:op3];
  • 不可以迴圈依賴 js [op1 addDependency:op3]; [op3 addDependency:op1];
  • 設定監聽:監聽一個操作的執行完畢 js [op2 setCompletionBlock:^{ NSLog(@"2執行完成"); }];

示例:

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

NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"1---%@",[NSThread currentThread]); }];

NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"2---%@",[NSThread currentThread]); }];

NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"3---%@",[NSThread currentThread]); }];

//設定依賴(op1和op3執行完之後才執行2) [op1 addDependency:op3]; [op2 addDependency:op1];

[queue addOperation:op1]; [queue addOperation:op2]; [queue addOperation:op3];

//監聽一個操作的執行完成 [op2 setCompletionBlock:^{ NSLog(@"2執行完成"); }]; ``` log:(依賴可知優先順序:op3 > op1 > op2,監聽的操作不一定和被監聽的操作同一個執行緒,都是非同步的,只是op3執行結束,肯定會執行監聽的操作) Snip20211110_23.png

監控NSOperation物件的屬性

  • isExecuting 表示任務正在執行中
  • isFinished 表示任務已經執行完成,被取消也算執行完成
  • isCancelled 表示任務已經取消執行
  • isAsynchronous 表示任務是併發還是同步執行
  • isReady 表示任務是併發還是同步執行

佇列裡的優先順序

  • iOS8以前,NSOperation 通過設定 queuePriority 屬性來設定優先順序
  • iOS 8.0後,NSOperation 通過設定 qualityOfService 來設定優先順序
  • 優先順序高的先執行,低的後執行 ```js //iOS8以前的優先順序(queuePriority) typedef NS_ENUM(NSInteger, NSOperationQueuePriority) { NSOperationQueuePriorityVeryLow = -8L, NSOperationQueuePriorityLow = -4L, NSOperationQueuePriorityNormal = 0, NSOperationQueuePriorityHigh = 4, NSOperationQueuePriorityVeryHigh = 8 };

//iOS8以後的優先順序(qualityOfService) typedef NS_ENUM(NSInteger, NSQualityOfService) { NSQualityOfServiceUserInteractive = 0x21,//最高優先順序,用於使用者互動事件 NSQualityOfServiceUserInitiated = 0x19,//次高優先順序,用於使用者需要馬上執行的事件 NSQualityOfServiceUtility = 0x11,//預設優先順序,主執行緒和沒有設定優先順序的執行緒都預設為這個優先順序 NSQualityOfServiceBackground = 0x09,//普通優先順序,用於普通任務 NSQualityOfServiceDefault = -1//最低優先順序,用於不重要的任務 } ```

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

NSBlockOperation op1 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"down1---%@",[NSThread currentThread]); }]; NSBlockOperation op2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"down2---%@",[NSThread currentThread]);

}];

NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"down3---%@",[NSThread currentThread]);

}];

//設定優先順序 op3.qualityOfService = NSQualityOfServiceUserInteractive;

[queue addOperation:op1]; [queue addOperation:op2]; [queue addOperation:op3]; ``` log: Snip20211110_24.png