iOS中的多執行緒
多執行緒的一些相關概念
什麼是程序
- 在系統中正在執行的一個應用程式。
- 每個程序之間是獨立的,每個程序均執行在其專用而且受保護的記憶體空間內。
什麼是執行緒
- 一個程序要想執行任務,必須得有一個執行緒,而且每一個程序中至少有一個執行緒
- 程序的所有任務都線上程中執行
什麼是執行緒的序列
- 一個執行緒中的任務都是序列執行的
- 如果要在一個執行緒中執行多個任務,那麼只能一個一個按順序來執行這些任務
- 同一時間內,一個執行緒只能執行一個任務
比如在一個執行緒中下載幾個檔案(檔案A,檔案B,檔案C)
多執行緒
什麼是多執行緒
- 一個程序中可以開啟多條執行緒,每條執行緒可以並行執行不同的任務
比如同時開啟三條執行緒分別下載幾個檔案(檔案A,檔案B,檔案C)
多執行緒原理
1、同一時間,CPU只能處理一條執行緒,只有一條執行緒在工作 2、多執行緒併發,其實是CPU快速的在多條執行緒之間切換 3、如果多執行緒切換速度特別快,就造成了多執行緒併發執行的假象 **注:**如果執行緒非常多,CPU會在N多執行緒之間切換,CPU消耗就會特別大,每條執行緒被呼叫額頻率就會被降低,所有要適當使用多執行緒
多執行緒優缺點
優點
1、能適當的提高程式執行效率 2、能夠適當的提高資源利用率(CPU,記憶體利用率)
缺點
1、建立執行緒是有開銷的,iOS下主要成本包括:核心資料結構(大約1KB),棧空間(子執行緒512KB,主執行緒1MB,也可以使用-setStackSize:設定,但是必須是4K的倍數,而且最小是16K),建立執行緒大約需要90毫秒的建立時間 2、若開啟大量執行緒,會降低執行緒上的效能,CPU消耗越大 3、多執行緒使用太多,會使程式設計更加複雜,比如執行緒間的通訊,資料共享等等
多執行緒在iOS開發中的應用
什麼是主執行緒
- 一個iOS程式執行後,預設會開啟的一條執行緒,稱為:主執行緒或UI執行緒
主執行緒作用
- 顯示\重新整理介面
- 處理UI事件,比如點選事件、滾動事件、拖拽事件等等
主執行緒使用注意
- 不要把耗時的操作放在主執行緒中
- 耗時的操作會卡住主執行緒,嚴重影響流暢度
耗時操作的執行如果把耗時操作放在主執行緒裡,當用戶在第5秒時點選按鈕時,因為執行緒裡的操作必須是序列的,此時的這個點選事件會排在10秒耗時操作之後,直到10秒的耗時操作結束後,才能執行按鈕點選事件,這樣就會造成UI卡住的現象,如下圖:
如果把耗時操作放在子執行緒裡,此刻,主執行緒和子執行緒同事進行,當用戶在第5秒時點選按鈕時,子執行緒做耗時操作,主執行緒響應介面操作,所以這樣就不會造成UI卡住的現象,所有要把耗時操作放在子執行緒後臺執行緒中,如圖:
iOS中多執行緒的實現方案
關於Pthread
- (IBAction)buttonClick:(id)sender {
//PThread的建立
pthread_t thread;
pthread_create(&thread, NULL, run, NULL);
//PThread的建立
pthread_t thread1;
pthread_create(&thread1, NULL, run, NULL);
}
void * run(void *param){
NSLog(@"當前執行緒--%@",[NSThread currentThread]);
for (NSInteger i = 0; i<5000; i++) {
NSLog(@"-buttonClick-%ld-%@",(long)i,[NSThread currentThread]);
}
return NULL;
}
從程式碼中可以看出Pthread的建立執行其實也是比較簡單的,不過實現過程是通過C語言進行的,從建立方法pthread_create(<#pthread_t _Nullable restrict _Nonnull#>, <#const pthread_attr_t restrict _Nullable#>, <#void _Nullable ( _Nonnull)(void * _Nullable)#>, <#void *restrict _Nullable#>)可以看出,第一個引數是需要一個Pthread 物件指標,第三個是需要一個C語言函式方法(就當於OC中繫結的執行方法),至於第二個和第四個引數,暫時沒有什麼用,可以直接傳入NULL 數字1811表示的是當前程式所處的程序 ID 數字27155和27156則表示當前所處的子執行緒 ID number也可以作為執行緒的標識 上述程式碼沒有給執行緒起名字,因此為null 所以我們就可以通過執行緒ID進行判斷是否成功開啟了一個子執行緒
關於NSThread
- 通過alloc init進行建立
//建立執行緒
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(run:) object:@"jack"];
thread.name = @"my-thread";
thread.threadPriority = 0.1;
//啟動執行緒
[thread start];
- 通過 detachNewThreadSelector 方式建立並執行執行緒
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"rose"];
- 隱式建立後自動啟動執行緒
[self performSelectorInBackground:@selector(run:) withObject:@"wahaha"];
採用第一種方式:設定一些執行緒屬性;例如執行緒 名字,從控制檯資訊可以看出來,當設定了不同的NSThread物件的優先順序屬性,可以控制其執行的順序,優先順序越高,越先執行;而設定名字屬性後,可以通過除錯監控當前所處執行緒,便於問題分析
第二、三種,建立和操作簡單
-
執行緒的狀態
當我們新建一個執行緒物件的時候,系統就會為其分配一塊記憶體,當你呼叫執行緒的開始方法時,就相當於把這個執行緒放在了執行緒池裡面,等待CPU去呼叫它,當執行緒池中有多個執行緒,那麼CPU就會在這幾個執行緒之間來回切換,但是當執行緒呼叫了sleep或同步鎖時,該呼叫的執行緒就會被阻塞,當sleep或鎖結束時,CPU再次進行切換中,當執行緒任務執行完,該執行緒就會釋放。
@property (class, readonly, strong) NSThread *currentThread// 獲取當前執行緒
+ (NSThread *)mainThread; // 獲得主執行緒
- (BOOL)isMainThread; // 是否為主執行緒
+ (BOOL)isMainThread; // 是否為主執行緒
- (void)start; // 進入就緒狀態 -> 執行狀態。當執行緒任務執行完畢,自動進入死亡狀態
+ (void)sleepUntilDate:(NSDate *)date;// 進入阻塞狀態
+ (void)sleepForTimeInterval:(NSTimeInterval)time;// 進入阻塞狀態
+ (void)exit;//強制停止執行緒,一旦執行緒停止(死亡)了,就不能再次開啟任務
多執行緒的競爭(執行緒鎖)
一塊資源可能被多個執行緒共享,也就是多個執行緒可能會訪問同一個資源、同一個物件、同一個變數,就會出現執行緒安全問題 當執行緒A去訪問檔案時,需要將檔案鎖住,讀取並運算結束後,將其解鎖,執行緒B再去訪問,執行緒B訪問時需要再次將檔案加鎖並運算,結束後再解鎖。這樣就避免了倆個執行緒同時訪問檔案而造成檔案錯誤。
互斥鎖使用
- 格式:
@synchronized(鎖物件) { // 需要鎖定的程式碼 }
- **互斥鎖的使用前提:**多條執行緒搶奪同一塊資源
- **注意:**鎖定1份程式碼只用1把鎖,用多把鎖是無效的
- 互斥鎖的優缺點
- 優點:能有效防止因多執行緒搶奪資源造成的資料安全問題
- 缺點:需要消耗大量的CPU資源
- 執行緒同步
- 執行緒同步的意思是:多條執行緒在同一條線上執行(按順序地執行任務)
- 互斥鎖,就是使用了執行緒同步技術,保證執行緒的序列
- 執行緒非同步
- 多執行緒預設的就是非同步,這個不需要多做解釋
- 原子和非原子屬性
- atomic:原子屬性,預設為setter方法加鎖(預設就是atomic),執行緒安全,需要消耗大量的資源
- nonatomic:非原子屬性,不會為setter方法加鎖,適合記憶體小的移動裝置
- 注:iOS開發中,所有屬性都宣告為nonatomic,儘量避免多執行緒搶奪同一資源。儘量將加鎖,資源搶奪業務交給伺服器
執行緒間通訊
- 執行緒間通訊 在一個程序中,執行緒往往不是孤立存在,多個執行緒需要經常進行通訊,一般表現為一個執行緒傳遞資料給另外一個執行緒,或者在一個執行緒中執行完一個特定任務後,轉到另一個執行緒繼續執行任務
- 執行緒間通訊常用方法
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait; - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
- 示例 處理UI在主執行緒,下載圖片載子執行緒,當圖片下載完後,迴歸主執行緒並顯示。
- (IBAction)showImageView:(UIButton *)sender {
NSLog(@"%@",[NSThread currentThread]);
[self performSelectorInBackground:@selector(download) withObject:nil];
}
-(void)download{
NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image =[UIImage imageWithData:data];
NSLog(@"%@",[NSThread currentThread]);
[self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:YES];
}
-(void)showImage:(UIImage *)image{
self.imageView.image = image;
NSLog(@"%@",[NSThread currentThread]);
}
關於GCD
- 什麼是GCD 全稱Grand Central Dispatch,純C語言,提供了非常大的函式
- GCD優勢 CGD是蘋果公司為多核的並行運算提供的方案(iOS4開始) GCD會自動利用更多的CPU核心 GCD會自動管理執行緒生命週期(建立執行緒、排程執行緒、銷燬執行緒)程式設計師只需要告訴GCD想要執行什麼任務,不需要編寫任何執行緒管理的程式碼
- GCD倆個核心概念 任務:執行什麼操作 佇列:用來存放任務
- GCD使用 定製任務(想做的操作) 將任務新增到隊列當中(GCD會自動將佇列中的任務取出,放到對應的執行緒中執行,任務的取出遵循佇列的FIFO原則,先進先出,後進後出。)
- GCD執行
//同步方式執行 dispatch_sync(dispatch_queue_t _Nonnull queue, <^(void)block>) //非同步的方式執行 dispatch_async(dispatch_queue_t _Nonnull queue, <^(void)block>) queue:佇列 block:任務
- 同步和非同步的區別:
- 同步:只能在當前執行緒中執行任務,不具備開啟新執行緒的能力
- 非同步:可以在新的執行緒中執行任務,具備開啟新執行緒的能力
- GCD的佇列型別
- 併發佇列:可以讓多個任務併發執行,併發功能只有在非同步dispatch_async函式下才有效
- 序列佇列:讓任務一個接著一個地執行
注:有四個容易混淆的概念:同步、非同步、併發、序列 同步和非同步主要影響能不能開執行緒 序列和並行主要影響任務的執行方式
GCD併發佇列
- 非同步函式 + 併發佇列:會開新執行緒
//1、建立一個併發佇列
//第一種:自定義(佇列的名字,佇列的型別)
//佇列的型別(併發:DISPATCH_QUEUE_CONCURRENT,序列:DISPATCH_QUEUE_SERIAL)
// dispatch_queue_t queue = dispatch_queue_create("com.xiaoyi.queue", DISPATCH_QUEUE_CONCURRENT);
//第二種:獲得全域性的併發佇列(引數1:優先順序(官方建議用default),引數2:目前沒有意義,官方文件提示傳0)
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//2、將任務加入佇列
dispatch_async(queue, ^{
for (NSInteger i = 0; i<5; i++) {
NSLog(@"1--%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i<5; i++) {
NSLog(@"2--%@",[NSThread currentThread]);
}
});
- 同步函式 + 併發佇列 :不會開新執行緒
//1、建立一個併發佇列
//第一種:自定義(佇列的名字,佇列的型別)
//佇列的型別(併發:DISPATCH_QUEUE_CONCURRENT,序列:DISPATCH_QUEUE_SERIAL)
// dispatch_queue_t queue = dispatch_queue_create("com.xiaoyi.queue", DISPATCH_QUEUE_CONCURRENT);
//第二種:獲得全域性的併發佇列(引數1:優先順序(官方建議用default),引數2:目前沒有意義,官方文件提示傳0)
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//2、將任務加入佇列
dispatch_sync(queue, ^{
for (NSInteger i = 0; i<5; i++) {
NSLog(@"1--%@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (NSInteger i = 0; i<5; i++) {
NSLog(@"2--%@",[NSThread currentThread]);
}
});
GCD序列佇列
- 非同步函式 + 序列佇列 :會開新執行緒,但是任務是序列的,執行完一個再執行下一個
//1、建立一個序列佇列(沒有全域性,只能建立)
//第一種:佇列的名字,佇列的型別
//佇列的型別(併發:DISPATCH_QUEUE_CONCURRENT,序列:DISPATCH_QUEUE_SERIAL)
dispatch_queue_t queue = dispatch_queue_create("com.xiaoyi.queue", DISPATCH_QUEUE_SERIAL);
//第二種:獲得全域性的併發佇列(引數1:佇列的名字,引數2:佇列的型別(NULL)
// dispatch_queue_t queue = dispatch_queue_create("com.xiaoyi.queue", NULL);
//2、將任務加入佇列
dispatch_async(queue, ^{
for (NSInteger i = 0; i<5; i++) {
NSLog(@"1--%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i<5; i++) {
NSLog(@"2--%@",[NSThread currentThread]);
}
});
- 同步函式 + 序列佇列 :不會開新執行緒,在當前執行緒執行任務
//1、建立一個序列佇列(沒有全域性,只能建立)
//第一種:佇列的名字,佇列的型別
//佇列的型別(併發:DISPATCH_QUEUE_CONCURRENT,序列:DISPATCH_QUEUE_SERIAL)
dispatch_queue_t queue = dispatch_queue_create("com.xiaoyi.queue", DISPATCH_QUEUE_SERIAL);
//第二種:獲得全域性的併發佇列(引數1:佇列的名字,引數2:佇列的型別(NULL)
// dispatch_queue_t queue = dispatch_queue_create("com.xiaoyi.queue", NULL);]
//2、將任務加入佇列
dispatch_sync(queue, ^{
for (NSInteger i = 0; i<5; i++) {
NSLog(@"1--%@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (NSInteger i = 0; i<5; i++) {
NSLog(@"2--%@",[NSThread currentThread]);
}
});
GCD主佇列(特殊的序列佇列)
-
放在主佇列中的任務,都會放在主執行緒中執行
-
使用dispatch_get_main_queue()獲得主佇列
-
非同步函式 + 主佇列 :只在主執行緒中執行任務
//獲取主佇列
dispatch_queue_t queue = dispatch_get_main_queue();
//2、將任務加入佇列
dispatch_async(queue, ^{
for (NSInteger i = 0; i<5; i++) {
NSLog(@"1--%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i<5; i++) {
NSLog(@"2--%@",[NSThread currentThread]);
}
});
- 同步函式 + 主佇列 :會卡死,執行緒間相互制約
//獲取主佇列
dispatch_queue_t queue = dispatch_get_main_queue();
//2、將任務加入佇列
dispatch_sync(queue, ^{
for (NSInteger i = 0; i<5; i++) {
NSLog(@"1--%@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (NSInteger i = 0; i<5; i++) {
NSLog(@"2--%@",[NSThread currentThread]);
}
});
-
各種佇列的執行效果 注: 1、使用同步函式往當前序列佇列中新增任務,會卡住當前的序列佇列 2、使用非同步函式是需要把當前方法執行,再去執行非同步函式
-
GCD執行緒間通訊
//圖片子執行緒下載
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image =[UIImage imageWithData:data];
//圖片主執行緒顯示
dispatch_sync(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
});
- barrier函式 在barrier函式前面執行的任務執行結束後它後面的任務才會執行 這個queue不能是全域性的佇列,最好自己建立,例如:
dispatch_queue_t queue = dispatch_queue_create("com.xiaoyi.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for (NSInteger i = 0; i<3; i++) {
NSLog(@"1----%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i<3; i++) {
NSLog(@"2----%@",[NSThread currentThread]);
}
});
dispatch_barrier_sync(queue, ^{
NSLog(@"--barrier--%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i<3; i++) {
NSLog(@"3----%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i<3; i++) {
NSLog(@"4----%@",[NSThread currentThread]);
}
});
- 延時函式
//第一種延時
[self performSelector:@selector(run) withObject:nil afterDelay:2];
//第二種延時
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"run");
});
//第三種延時
[NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(run) userInfo:nil repeats:NO];
NSLog(@"start");
- 一次性程式碼
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"--程式在整個載入過程只會載入一次");
});
- 快速迭代(主要用於併發佇列)
//引數1: 指定重複次數
//引數2:物件的DispatchQueue
//引數3:帶有引數的Block, index的作用是為了按執行的順序區分各個Block
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(10, queue, ^(size_t index) {
NSLog(@"%@--%zu",[NSThread currentThread],index);
});
NSLog(@"done");
GCD佇列組
- 佇列組就是可以對多個佇列進行操作的一個組,在佇列組中可以對不同佇列進行操作監聽結果等等
//建立一個佇列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//建立一個佇列組
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
NSLog(@"download image1 start");
//下載圖片1
NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
self.image1 =[UIImage imageWithData:data];
NSLog(@"download image1 end");
});
dispatch_group_async(group, queue, ^{
NSLog(@"download image2 start");
//下載圖片2
NSURL *url = [NSURL URLWithString:@"http://pic38.nipic.com/20140228/5571398_215900721128_2.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
self.image2 =[UIImage imageWithData:data];
NSLog(@"download image2 end");
});
//當這個佇列組的所有佇列全部完成,就會收到這個訊息
dispatch_group_notify(group, queue, ^{
NSLog(@"download all images");
//合成新圖片
UIGraphicsBeginImageContext(CGSizeMake(100, 100));
[self.image1 drawInRect:CGRectMake(0, 0, 50, 100)];
[self.image2 drawInRect:CGRectMake(50, 0, 50, 100)];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//在主執行緒顯示
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
});
關於NSOperation
- 配合使用NSOperation和NSOperationQueue也能實現多執行緒程式設計
- 先將需要執行的操作封裝到一個NSOperation物件中
- 然後將NSOperation物件新增到NSOperationQueue中
- 系統會自動將NSOperationQueue中的NSOperation取出來
- 將取出的NSOperation封裝的操作放到一條新執行緒中執行
- NSOperation的子類(抽象類,並不具備封裝操作的能力,必須使用它的子類)
- NSInvocationOperation
- NSBlockOperation
- 自定義子類繼承NSOperation,實現內部相應的方法
- 關於NSInvocationOperation 呼叫start方法開始執行操作,一旦執行操作就會呼叫run方法 注:預設情況下,呼叫了start方法後並不會開一條新執行緒去執行操作,而是在當前執行緒同步執行操作 只有NSOperation放到一個NSOperationQueue中,才會非同步執行
- (IBAction)invocationOperation:(id)sender {
//初始化Operation子類
NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run) object:nil];
//開啟
[operation start];
}
-(void)run{
NSLog(@"0--%@",[NSThread currentThread]);
}
- 關於NSBlockOperation 注:只要NSBlockOperation封裝的運算元大於1,就會非同步執行
//初始化Operation子類
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1--%@",[NSThread currentThread]);
}];
//新增額外的任務(在子執行緒執行)
[operation addExecutionBlock:^{
NSLog(@"2--%@",[NSThread currentThread]);
}];
[operation addExecutionBlock:^{
NSLog(@"3--%@",[NSThread currentThread]);
}];
[operation start];
- 關於NSOperationQueue
- NSOperationQueue的作用
- NSOperation可以呼叫start方法來執行任務,預設是同步的
- 如果將NSOperation新增到NSOperationQueue(操作佇列)中,系統會自動非同步執行NSOperation中的操作
- NSOperationQueue的作用
//建立佇列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//NSOperationQueue *queue = [NSOperationQueue mainQueue];
//建立操作(任務)
//建立--NSInvocationOperation
NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run1) object:nil];
NSInvocationOperation *op2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run2) object:nil];
//建立--NSBlockOperation
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"-1--%@",[NSThread currentThread]);
}];
NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"-2--%@",[NSThread currentThread]);
}];
//自定義(需要繼承NSOperation,執行的操作需要放在這個自定義類的main中)
SSOperation *op5 = [[SSOperation alloc]init];
//新增任務佇列中
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
[queue addOperation:op4];
[queue addOperation:op5];
//也可以直接建立任務到佇列中去
[queue addOperationWithBlock:^{
NSLog(@"-3--%@",[NSThread currentThread]);
}];
通過輸出的log可以看出確實有開啟執行緒,所有隻要將操作新增的佇列裡,就可以實現多執行緒操作
- NSOperationQueue設定最大併發運算元
//建立佇列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//設定最大併發運算元(不管加入佇列有多少操作,實際佇列併發數為3)
queue.maxConcurrentOperationCount = 3;
// //設定為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]);
}];
[queue addOperationWithBlock:^{
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"-4--%@",[NSThread currentThread]);
}];
- NSOperationQueue設定佇列掛起與取消
- 當佇列呼叫了佇列掛起的方法( self.queue.suspended = YES;),佇列裡的執行方法立即停止,但是有一點需要注意的是,當block操作中,佇列掛起是不起作用的,它是無法停止的,必須操作執行結束後才會生效。
- 當佇列呼叫取消( [self.queue cancelAllOperations])就意味著後續佇列不再執行,再次啟動需要重新加入佇列
#pragma mark-OperationQueue相關設定--設定佇列掛起(暫停)
- (IBAction)createOperationQueueSuspended:(id)sender {
//建立佇列
self.queue = [[NSOperationQueue alloc]init];
self.queue.maxConcurrentOperationCount = 1;
[self.queue addOperationWithBlock:^{
NSLog(@"-1--%@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
[self.queue addOperationWithBlock:^{
NSLog(@"-2--%@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
[self.queue addOperationWithBlock:^{
NSLog(@"-3--%@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
[self.queue addOperationWithBlock:^{
NSLog(@"-4--%@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
//設定佇列掛起或者取消的話都必須是在block方法執行完之後才有效
[self.queue addOperationWithBlock:^{
for(NSInteger i = 0;i<5;i++){
NSLog(@"-5--%zd---%@",(long)i,[NSThread currentThread]);
}
}];
}
#pragma mark-設定佇列掛起
- (IBAction)operationSetSuspended:(id)sender {
if(self.queue.suspended){
//恢復佇列,繼續執行
self.queue.suspended = NO;
}else{
//掛起(暫停佇列)
self.queue.suspended = YES;
}
}
#pragma mark-設定佇列取消(取消就意味著後續佇列不再執行,再次啟動需要重新加入佇列)
- (IBAction)operationSetCancel:(id)sender {
[self.queue cancelAllOperations];
}
- NSOperationQueue設定佇列監聽與依賴
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]);
}];
NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"down4---%@",[NSThread currentThread]);
}];
//設定依賴(op1和op3執行完之後才執行2)
[op3 addDependency:op1];
[op3 addDependency:op4];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
[queue addOperation:op4];
//監聽一個操作的執行完成
[op3 setCompletionBlock:^{
NSLog(@"執行完成");
}];
注:一定要避免相互依賴,比如
[op3 addDependency:op1];
[op1 addDependency:op3]; //錯誤的寫法---相互依賴
- NSOperationQueue佇列間的資料通訊 先建立了一個普通佇列,在普通佇列裡執行倆個操作,當子執行緒的圖片都下載下來後,迴歸主執行緒將其顯示在UI介面上。
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
__block UIImage *image1;
NSBlockOperation *downloadw1 = [NSBlockOperation blockOperationWithBlock:^{
//下載圖片1
NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
image1 =[UIImage imageWithData:data];
}];
__block UIImage *image2;
NSBlockOperation *downloadw2 = [NSBlockOperation blockOperationWithBlock:^{
//下載圖片2
NSURL *url = [NSURL URLWithString:@"http://pic38.nipic.com/20140228/5571398_215900721128_2.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
image2 =[UIImage imageWithData:data];
}];
NSBlockOperation *combine = [NSBlockOperation blockOperationWithBlock:^{
//合成新圖片
UIGraphicsBeginImageContext(CGSizeMake(100, 100));
[image1 drawInRect:CGRectMake(0, 0, 50, 100)];
[image2 drawInRect:CGRectMake(50, 0, 50, 100)];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[[NSOperationQueue mainQueue]addOperationWithBlock:^{
self.imageView.image = image;
}];
}];
[combine addDependency:downloadw1];
[combine addDependency:downloadw2];
[queue addOperation:downloadw1];
[queue addOperation:downloadw2];
[queue addOperation:combine];