線程基礎及NSThread,GCD,NSOperation簡單使用

一.多線程

  • 進程是指在系統中正在運行的一個應用程序。每個進程之間是獨立的,每個進程均運行在其專用且受保護的內存空間內。(比如同時打開迅雷、Xcode,系統就會分別啟動2個進程。)

  • 線程是進程的基本執行單元,1個進程要想執行任務,必須得有線程(每1個進程至少要有1條線程,并且1個線程中任務的執行是串行的)。

  • 多線程是指1個進程中可以開啟多條線程,多條線程可以并行(同時)執行不同的任務。

1.線程的并行:
 并行即同時執行。比如同時開啟3條線程分別下載3個文件(分別是文件A、文件B、文件C)
2.多線程并發執行的原理:
 a. 在同一時間里,CPU只能處理 1 條線程,只有 1 條線程在工作(執行)
 b. 多線程并發(同時)執行,其實是CPU快速地在多條線程之間調度(切換),如果CPU調度線程的時間足夠快,就造成了多線程并發執行的假象
3.優缺點

3.1優點

 a. 能適當提高程序的執行效率
 b. 能適當提高資源利用率(CPU、內存利用率)

3.2缺點

 a. 開啟線程需要占用一定的內存空間(默認情況下,主線程占用1M,子線程占用512KB),如果開啟大量的線程,會占用大量的內存空間,降低程序的性能
 b. 線程越多,CPU在調度線程上的開銷就越大
 c. 程序設計更加復雜:比如線程之間的通信、多線程的數據共享

4.多線程在ios中的應用

4.1主線程

 a. 一個iOS程序運行后,默認會開啟 1 條線程,稱為“主線程”或“UI 線程”
 b. 作用:刷新顯示 UI,處理 UI 事件

4.2子線程

 a. 除了主線程之外的所有線程,也叫做后臺線程
 b. 子線程不能用來刷新 UI

注意:

 a. 不要將耗時操作放到主線程中去處理,因為會卡住線程,造成畫面卡頓的現象
 b. 和UI相關的刷新操作必須放到主線程中進行處理
5.多線程安全隱患
  • 資源共享
1塊資源可能會被多個線程共享,也就是多個線程可能會訪問同一塊資源
比如多個線程訪問同一個對象、同一個變量、同一個文件
  • 當多個線程訪問同一塊資源時,很容易引發數據錯亂和數據安全問題
安全隱患解決– 互斥鎖
  • 互斥鎖使用格式
// 注意:鎖定1份代碼只用1把鎖,用多把鎖是無效的
@synchronized(鎖對象){ //需要鎖定的代碼  }
  • 互斥鎖的優缺點
優點:能有效防止因多線程搶奪資源造成的數據安全問題
缺點:需要消耗大量的CPU資源
  • 互斥鎖的使用前提:
多條線程搶奪同一塊資源
  • 線程同步: 多條線程在同一條線上執行(按順序地執行任務)
互斥鎖,就是使用了線程同步技術
6.線程間通信
1個線程傳遞數據給另1個線程
在1個線程中執行完特定任務后,轉到另1個線程繼續執行任務

二.ios中常用多線程方案

  • NSThread

1.創建方式

//1.實例方法
 NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(loadImage) object:nil];
[thread start];
//2.類方法
 [NSThread detachNewThreadSelector:@selector(loadImage) toTarget:self withObject:nil];
//3.//隱式創建線程
 [self performSelectorInBackground:@selector(loadImage) withObject:self];

2.當前線程獲取

NSThread *current = [NSThread currentThread];

3.獲取主線程

NSThread *main = [NSThread mainThread];
//判斷是否為主線程
-(BOOL)isMainThread;
+(BOOL)isMainThread;

4.暫停當前線程

[NSThread sleepForTimeInterval:2];

5.強制停止

+(void)exit;

6.線程之間通信

//在指定線程上執行操作
[self performSelector:@selector(run) onThread:thread withObject:nil waitUntilDone:YES]; 
//在主線程上執行操作
[self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:YES]; 
//在當前線程執行操作
[self performSelector:@selector(run) withObject:nil];

7.簡單使用

//創建線程
-(void)implicitCreateThread{

[self performSelectorInBackground:@selector(loadImageSource:) withObject:imgUrl];

}
//下載圖片數據

-(void)loadImageSource:(NSString *)url{

    NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]];
    UIImage *image = [UIImage imageWithData:imgData];
    
    if (imgData!= nil) {
        //主線程中跟新界面
        [self performSelectorOnMainThread:@selector(refreshImageView:) withObject:image waitUntilDone:YES];
    }else{
        NSLog(@"NoData");
    }
}
//跟新界面
-(void)refreshImageView:(UIImage *)image{

[self.imageView setImage:image];

}
  • GCD

一.GCD中主要核心概念為任務隊列

  • 任務:你在線程中執行的那段代碼。分為以下兩種.
同步執行(sync):只能在當前線程中執行任務,不具備開啟新線程的能力
異步執行(async):可以在新的線程中執行任務,具備開啟新線程的能力
  • 隊列:用來存放任務的隊。采用FIFO(先進先出)的原則,即新任務總是被插入到隊列的末尾,而讀取任務的時候總是從隊列的頭部開始讀取。分為以下兩種.
并發隊列(Concurrent Dispatch Queue):可以讓多個任務并發(同時)執行(自動開啟多個線程同時執行任務)
并發功能只有在異步(dispatch_async)函數下才有效
串行隊列(Serial Dispatch Queue):讓任務一個接著一個地執行(一個任務執行完畢后,再執行下一個任務)

二.GCD創建

GCD創建分為兩步

1.創建一個隊列(串行隊列或并發隊列)
2.將任務添加到隊列中,然后系統就會根據任務類型執行任務(同步執行或異步執行)
下邊來看看隊列的創建方法和任務的創建方法。
1.隊列的創建方法
// 串行隊列的創建方法
dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);
// 并發隊列的創建方法
dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);
//主隊列,GCD自帶的一種特殊的串行隊列
dispatch_queue_t queue = dispatch_get_main_queue();
對于并發隊列,還可以使用dispatch_get_global_queue來創建全局并發隊列。GCD默認提供了全局的并發隊列,需要傳入兩個參數。第一個參數表示隊列優先級,一般用DISPATCH_QUEUE_PRIORITY_DEFAULT。第二個參數暫時沒用,用0即可。
2.任務的創建方法
// 同步執行任務創建方法
dispatch_sync(queue, ^{
    NSLog(@"%@",[NSThread currentThread]);    // 這里放任務代碼
});
// 異步執行任務創建方法
dispatch_async(queue, ^{
    NSLog(@"%@",[NSThread currentThread]);    // 這里放任務代碼
});



這里根據隊列和任務的不同共有6種組合方式。

并發隊列 串行隊列 主隊列
同步(sync) 沒有開啟新線程,串行執行任務 沒有開啟新線程,串行執行任務 沒有開啟新線程,串行執行任務
異步(async) 有開啟新線程,并發執行任務 有開啟新線程(1條),串行執行任務 沒有開啟新線程,串行執行任務
  • 并發隊列 + 同步執行

不會開啟新線程,執行完一個任務,再執行下一個任務

- (void) syncConcurrent
{
    NSLog(@"syncConcurrent---begin");

    dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);

    dispatch_sync(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"1------%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"2------%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"3------%@",[NSThread currentThread]);
        }
    });

    NSLog(@"syncConcurrent---end");
}
輸出結果:
2016-09-03 19:22:27.577 GCD[11557:1897538] syncConcurrent---begin
2016-09-03 19:22:27.578 GCD[11557:1897538] 1------<NSThread: 0x7f82a1d058b0>{number = 1, name = main}
2016-09-03 19:22:27.578 GCD[11557:1897538] 1------<NSThread: 0x7f82a1d058b0>{number = 1, name = main}
2016-09-03 19:22:27.578 GCD[11557:1897538] 2------<NSThread: 0x7f82a1d058b0>{number = 1, name = main}
2016-09-03 19:22:27.579 GCD[11557:1897538] 2------<NSThread: 0x7f82a1d058b0>{number = 1, name = main}
2016-09-03 19:22:27.579 GCD[11557:1897538] 3------<NSThread: 0x7f82a1d058b0>{number = 1, name = main}
2016-09-03 19:22:27.579 GCD[11557:1897538] 3------<NSThread: 0x7f82a1d058b0>{number = 1, name = main}
2016-09-03 19:22:27.579 GCD[11557:1897538] syncConcurrent---end
1.從并發隊列 + 同步執行中可以看到,所有任務都是在主線程中執行的。由于只有一個線程,所以任務只能一個一個執行。
2.同時我們還可以看到,所有任務都在打印的syncConcurrent-begin和syncConcurrent---end之間,這說明任務是添加到隊列中馬上執行的。
  • 并發隊列 + 異步執行

可同時開啟多線程,任務交替執行

- (void) asyncConcurrent
{
    NSLog(@"asyncConcurrent---begin");

    dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);

    dispatch_async(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"1------%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"2------%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"3------%@",[NSThread currentThread]);
        }
    });

    NSLog(@"asyncConcurrent---end");
}
輸出結果:
2016-09-03 19:27:31.503 GCD[11595:1901548] asyncConcurrent---begin
2016-09-03 19:27:31.504 GCD[11595:1901548] asyncConcurrent---end
2016-09-03 19:27:31.504 GCD[11595:1901626] 1------<NSThread: 0x7f8309c22080>{number = 2, name = (null)}
2016-09-03 19:27:31.504 GCD[11595:1901625] 2------<NSThread: 0x7f8309f0b790>{number = 4, name = (null)}
2016-09-03 19:27:31.504 GCD[11595:1901855] 3------<NSThread: 0x7f8309e1a950>{number = 3, name = (null)}
2016-09-03 19:27:31.504 GCD[11595:1901626] 1------<NSThread: 0x7f8309c22080>{number = 2, name = (null)}
2016-09-03 19:27:31.504 GCD[11595:1901625] 2------<NSThread: 0x7f8309f0b790>{number = 4, name = (null)}
2016-09-03 19:27:31.505 GCD[11595:1901855] 3------<NSThread: 0x7f8309e1a950>{number = 3, name = (null)}
1.在并發隊列 + 異步執行中可以看出,除了主線程,又開啟了3個線程,并且任務是交替著同時執行的。
2.另一方面可以看出,所有任務是在打印的syncConcurrent---begin和syncConcurrent---end之后才開始執行的。說明任務不是馬上執行,而是將所有任務添加到隊列之后才開始異步執行。
  • 串行隊列 + 同步執行

不會開啟新線程,在當前線程執行任務。任務是串行的,執行完一個任務,再執行下一個任務

- (void) syncSerial
{
    NSLog(@"syncSerial---begin");

    dispatch_queue_t queue = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);

    dispatch_sync(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"1------%@",[NSThread currentThread]);
        }
    });    
    dispatch_sync(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"2------%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"3------%@",[NSThread currentThread]);
        }
    });

    NSLog(@"syncSerial---end");
}
輸出結果為:
2016-09-03 19:29:00.066 GCD[11622:1903904] syncSerial---begin
2016-09-03 19:29:00.067 GCD[11622:1903904] 1------<NSThread: 0x7fa2e9f00980>{number = 1, name = main}
2016-09-03 19:29:00.067 GCD[11622:1903904] 1------<NSThread: 0x7fa2e9f00980>{number = 1, name = main}
2016-09-03 19:29:00.067 GCD[11622:1903904] 2------<NSThread: 0x7fa2e9f00980>{number = 1, name = main}
2016-09-03 19:29:00.067 GCD[11622:1903904] 2------<NSThread: 0x7fa2e9f00980>{number = 1, name = main}
2016-09-03 19:29:00.067 GCD[11622:1903904] 3------<NSThread: 0x7fa2e9f00980>{number = 1, name = main}
2016-09-03 19:29:00.068 GCD[11622:1903904] 3------<NSThread: 0x7fa2e9f00980>{number = 1, name = main}
2016-09-03 19:29:00.068 GCD[11622:1903904] syncSerial---end
1.在串行隊列 + 同步執行可以看到,所有任務都是在主線程中執行的,并沒有開啟新的線程。而且由于串行隊列,所以按順序一個一個執行。
2.同時我們還可以看到,所有任務都在打印的syncConcurrent---begin和syncConcurrent---end之間,這說明任務是添加到隊列中馬上執行的。
  • 串行隊列 + 異步執行

會開啟新線程,但是因為任務是串行的,執行完一個任務,再執行下一個任務

- (void) asyncSerial
{
    NSLog(@"asyncSerial---begin");

    dispatch_queue_t queue = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);

    dispatch_async(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"1------%@",[NSThread currentThread]);
        }
    });    
    dispatch_async(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"2------%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"3------%@",[NSThread currentThread]);
        }
    });

    NSLog(@"asyncSerial---end");
}
輸出結果為:
2016-09-03 19:30:08.363 GCD[11648:1905817] asyncSerial---begin
2016-09-03 19:30:08.364 GCD[11648:1905817] asyncSerial---end
2016-09-03 19:30:08.364 GCD[11648:1905895] 1------<NSThread: 0x7fb548c0e390>{number = 2, name = (null)}
2016-09-03 19:30:08.364 GCD[11648:1905895] 1------<NSThread: 0x7fb548c0e390>{number = 2, name = (null)}
2016-09-03 19:30:08.364 GCD[11648:1905895] 2------<NSThread: 0x7fb548c0e390>{number = 2, name = (null)}
2016-09-03 19:30:08.364 GCD[11648:1905895] 2------<NSThread: 0x7fb548c0e390>{number = 2, name = (null)}
2016-09-03 19:30:08.365 GCD[11648:1905895] 3------<NSThread: 0x7fb548c0e390>{number = 2, name = (null)}
2016-09-03 19:30:08.365 GCD[11648:1905895] 3------<NSThread: 0x7fb548c0e390>{number = 2, name = (null)}
1.在串行隊列 + 異步執行可以看到,開啟了一條新線程,但是任務還是串行,所以任務是一個一個執行。
2.另一方面可以看出,所有任務是在打印的syncConcurrent---begin和syncConcurrent---end之后才開始執行的。說明任務不是馬上執行,而是將所有任務添加到隊列之后才開始同步執行。

三.GCD之間的通訊

在iOS開發過程中,我們一般在主線程里邊進行UI刷新,例如:點擊、滾動、拖拽等事件。我們通常把一些耗時的操作放在其他線程,比如說圖片下載、文件上傳等耗時操作。而當我們有時候在其他線程完成了耗時操作時,需要回到主線程,那么就用到了線程之間的通訊。

 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSString * url = @"";
        NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]];
        UIImage *image = [UIImage imageWithData:imgData];
        
        // 回到主線程
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.imageView setImage:image];
        });
    });

4.GCD 其他用法

  • GCD的柵欄方法 dispatch_barrier_async

我們有時需要異步執行兩組操作,而且第一組操作執行完之后,才能開始執行第二組操作。這樣我們就需要一個相當于柵欄一樣的一個方法將兩組異步執行的操作組給分割起來,當然這里的操作組里可以包含一個或多個任務。這就需要用到dispatch_barrier_async方法在兩個操作組間形成柵欄。

- (void)barrier
{
    dispatch_queue_t queue = dispatch_queue_create("12312312", DISPATCH_QUEUE_CONCURRENT);

    dispatch_async(queue, ^{
        NSLog(@"----1-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"----2-----%@", [NSThread currentThread]);
    });

    dispatch_barrier_async(queue, ^{
        NSLog(@"----barrier-----%@", [NSThread currentThread]);
    });

    dispatch_async(queue, ^{
        NSLog(@"----3-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"----4-----%@", [NSThread currentThread]);
    });
}
輸出結果:
2016-09-03 19:35:51.271 GCD[11750:1914724] ----1-----<NSThread: 0x7fb1826047b0>{number = 2, name = (null)}
2016-09-03 19:35:51.272 GCD[11750:1914722] ----2-----<NSThread: 0x7fb182423fd0>{number = 3, name = (null)}
2016-09-03 19:35:51.272 GCD[11750:1914722] ----barrier-----<NSThread: 0x7fb182423fd0>{number = 3, name = (null)}
2016-09-03 19:35:51.273 GCD[11750:1914722] ----3-----<NSThread: 0x7fb182423fd0>{number = 3, name = (null)}
2016-09-03 19:35:51.273 GCD[11750:1914724] ----4-----<NSThread: 0x7fb1826047b0>{number = 2, name = (null)}

可以看出在執行完柵欄前面的操作之后,才執行柵欄操作,最后再執行柵欄后邊的操作。

  • GCD的延時執行方法 dispatch_after

當我們需要延遲執行一段代碼時,就需要用到GCD的dispatch_after方法。

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    // 2秒后異步執行這里的代碼...
   NSLog(@"run-----");
});
  • GCD的一次性代碼(只執行一次) dispatch_once

我們在創建單例、或者有整個程序運行過程中只執行一次的代碼時,我們就用到了GCD的dispatch_once方法。使用dispatch_once函數能保證某段代碼在程序運行過程中只被執行1次。

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    // 只執行1次的代碼(這里面默認是線程安全的)
});
  • GCD的隊列組 dispatch_group

有時候我們會有這樣的需求:分別異步執行2個耗時操作,然后當2個耗時操作都執行完畢后再回到主線程執行操作。這時候我們可以用到GCD的隊列組。

我們可以先把任務放到隊列中,然后將隊列放入隊列組中。
調用隊列組的dispatch_group_notify回到主線程執行操作。

dispatch_group_t group =  dispatch_group_create();

dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // 執行1個耗時的異步操作
});

dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // 執行1個耗時的異步操作
});

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    // 等前面的異步操作都執行完畢后,回到主線程...
});
  • NSOperation

1.簡介

  • NSOperation 是對 GCD 的一層封裝,更加面向對象。主要由NSOperation(封裝操作) 和 NSOperationQueue (隊列)實現多線程。

2 .NSOperation

  • NSOperation是一個抽象類,不能夠封裝操作,只能通過以下三種方式來實現封裝操作:
  • NSInvocationOperation
  • NSBlockOperation
  • 自定義子類繼承 NSOperation ,實現內部相應的方法
  • NSInvocationOperation
//創建NSInvocationOperation對象
  NSInvocationOperation *invoOperation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(doSomeThing) object:nil];
//調用start方法來執行操作
  [invoOperation start];

NSInvocationOperation比較簡單,繼承了NSOperation,它是基于一個對象和selector來創建操作,可以直接使用而不需繼承來實現自己的操作處理。并且默認情況下,調用了start方法之后不會開一條新線程去操作,是在當前線程下同步執行操作,只有將其放到NSOperationQueue中才會執行異步操作。

  • NSBlockOperation
//創建NSBlockOperation對象
 NSBlockOperation *blockOperation1 = [NSBlockOperation blockOperationWithBlock:^{
     NSLog(@"create1----%@",[NSThread currentThread]);
 }];

 //通過addExecutionBlock:方法添加更多的操作
 [blockOperation1 addExecutionBlock:^{
     NSLog(@"add1-----%@",[NSThread currentThread]);
 }];

 [blockOperation1 addExecutionBlock:^{
     NSLog(@"add2-----%@",[NSThread currentThread]);
 }];
[blockOperation1 start];
8975FE9D-D470-4860-B22C-E77601139C52.png

可以看出NSBlockOperation如果封裝了多個操作,那么除了第一個操作外,其他的操作會在子線程中進行。如果只封裝了一個操作,默認在主線程中進行,并且只要NSBlockOperation封裝的操作數大于一個,就會異步執行操作。

  • 定義繼承自NSOperation的子類
//繼承NSOpetaion

#import "BHOperation.h"

@implementation BHOperation

- (void)main{
    NSLog(@"%@",[NSThread currentThread]);
}


@end

 BHOperation *operation = [[BHOperation alloc]init];     
 [operation start];

3.NSOperationQueue

  • 主隊列
  • 凡是添加到主隊列中的任務(NSOperation),都會放到主線程中執行
NSOperationQueue *queue = [NSOperationQueue mainQueue];
  • 其他隊列
  • 添加到這種隊列中的任務(NSOperation),就會自動放到子線程中執行
  • 同時包含了:串行、并發功能
NSOperationQueue *queue = [[NSOperationQueue alloc] init];

4.將任務將入到隊列中

1.需要先創建任務,再將創建好的任務加入到創建好的隊列中去

- (void)addOperationToQueue
{
    // 1.創建隊列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    // 2. 創建操作  
    // 創建NSInvocationOperation    
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];    
    // 創建NSBlockOperation    
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^
            NSLog(@"op1-----%@", [NSThread currentThread]);
    }];

    // 3. 添加操作到隊列中:addOperation:   
    [queue addOperation:op1]; // [op1 start]    
    [queue addOperation:op2]; // [op2 start]
}

- (void)run
{
    NSLog(@"op2-----%@", [NSThread currentThread]);
}

2.無需先創建任務,在block中添加任務,直接將任務block加入到隊列中。

- (void)addOperationWithBlockToQueue
{
    // 1. 創建隊列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    // 2. 添加操作到隊列中:addOperationWithBlock:
    [queue addOperationWithBlock:^{
            NSLog(@"-----%@", [NSThread currentThread]);
    }];
}

5. 控制串行執行和并行執行的關鍵

這里有個關鍵參數maxConcurrentOperationCount,叫做最大并發數。

最大并發數:maxConcurrentOperationCount

  • maxConcurrentOperationCount默認情況下為-1,表示不進行限制,默認為并發執行。
  • 當maxConcurrentOperationCount為1時,進行串行執行。
  • 當maxConcurrentOperationCount大于1時,進行并發執行,當然這個值不應超過系統限制,即使自己設置一個很大的值,系統也會自動調整。

6.執行順序

對于添加到queue中的操作,它的執行順序取決于兩點:

1.首先是NSOperation是否準備好,是否準備好是由對象的依賴關系來決定。
  • 操作依賴

NSOperation之間可以設置依賴來保證執行順序,?如一定要讓操作A執行完后,才能執行操作B

- (void)addDependency
{
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

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

    [op2 addDependency:op1];    // 讓op2 依賴于 op1,則先執行op1,在執行op2

    [queue addOperation:op1];
    [queue addOperation:op2];
}
2.根據NSOperation的相對優先級來決定,默認都是普通的優先級,可以通過setQueuePriority來設置優先級。優先級不能替代依賴關系,優先級是針對于已經準備好的NSOperation來確定執行順序,先滿足依賴關系,再根據優先級從所有準備好的操作中選擇執行優先級最高的那個。
  • 設置優先級
//創建任務
NSBlockOperation *blockOperation1 = [NSBlockOperation blockOperationWithBlock:^{
      NSLog(@"create1----%@",[NSThread currentThread]);
  }];
//設置優先級
[blockOperation4 setQueuePriority:NSOperationQueuePriorityVeryHigh];
//加入隊列
  [myQueue addOperation:blockOperation1];
//優先級:
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
    NSOperationQueuePriorityVeryLow = -8L,
    NSOperationQueuePriorityLow = -4L,
    NSOperationQueuePriorityNormal = 0,
    NSOperationQueuePriorityHigh = 4,
    NSOperationQueuePriorityVeryHigh = 8
};
  • (注意)先滿足依賴關系,才根據優先級從所有準備好的操作中選擇優先級高的執行

7.其他

  • -(void)cancel;NSOperation提供的方法,可取消單個操作

  • -(void)cancelAllOperations;NSOperationQueue提供的方法,可以取消隊列的所有操作

  • -(void)setSuspended:(BOOL)b;可設置任務的暫停和恢復,YES代表暫停隊列,NO代表恢復隊列

  • -(BOOL)isSuspended;判斷暫停狀態

GCD,NSOpeation,NSThread優缺點

  • NSThread
    每個NSThread對象對應一個線程,真正最原始的線程。

  • 優點:NSThread 輕量級最低,相對簡單。

  • 缺點:手動管理所有的線程活動,如生命周期、線程同步、睡眠等。

  • NSOperation
    自帶線程管理的抽象類。

  • 優點:自帶線程周期管理,操作上可更注重自己邏輯。

  • 缺點:面向對象的抽象類,只能實現它或者使用它定義好的兩個子類:NSInvocationOperation 和 NSBlockOperation。

  • GCD
    Grand Central Dispatch (GCD)是Apple開發的一個多核編程的解決方法。

  • 優點:最高效,避開并發陷阱。

  • 缺點:基于C實現。

  • 選擇小結

  • 簡單而安全的選擇NSOperation實現多線程即可。

  • 處理大量并發數據,又追求性能效率的選擇GCD。

  • NSThread本人選擇基本上是在做些小測試上使用,當然也可以基于此造個輪子。

參考文獻

iOS 多線程基礎知識淺析

iOS多線程--徹底學會多線程之『GCD』

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容