##一、基本概念ios
什麼是進程: 進程是在系統運行的一個程序,每一個進程之間是獨立的,每一個進程均運行在其專有且受保護的內存空間內。git
什麼是線程: 一個進程想要執行任務,必須得有線程(至少一個線程),線程是進程的基本執行單位,一個進程的全部任務都必須在線程中執行。github
線程的串行: 一個線程中任務的執行是串行的,若是要在一個線程中執行多個任務,只能一個一個的按順序執行編程
##二、多線程安全
什麼是多線程:服務器
一個進程中能夠開啓多個線程,每一個線程能夠併發/並行執行不一樣的任務,多線程能夠提交程序的執行效率,好比同時執行任務ABC。多線程
多線程原理:併發
同一時間,CPU只能執行一個線程,只有一個線程正在執行,多線程併發執行,實際上是CPU快速的在多個線程之間切換,若是CPU的切換線程的時間足夠快,就會形成多線程併發執行的假象。框架
多線程的優缺點:異步
優勢: 1.能適當的提升程序的執行效率 2.能適當的提升資源的利用率 缺點: 1.開啓線程會佔用必定的內存空間(主線程1M,子線程0.5M),若是開啓過多的線程就會佔用大量的內存空間,下降程序的性能。 2.線程越多,CPU在調度線程上的開銷就越大。
##三、主線程
一個IOS程序運行之後,默認會開啓一個線程,這個線程就被稱爲主線程或(UI線程)。主線程的主要做用是顯示/刷新UI界面,處理UI事件(點擊,滾動,拖拽等)。
IOS中的多線程:
iOS中有四種多線程編程的技術:
1.Pthread (基本不會使用,線程的生命週期由咱們⾃己管理)
2.NSThread(每一個Thread對象對應⼀一個線程)(使⽤用得⽐比較少,線程的⽣生命週期由咱們⾃己管理)
3.NSOperation(面向對象的線程技術)(基於gcd來實現,常常使⽤,⽣命週期由系統管理)
4.GCD(是基於C語⾔言的框架,能夠充分利用多核,是蘋果推薦使⽤的多線程技術)(常用,生命週期由系統管理))
以上這四種編程⽅方式從上到下,抽象度層次是從低到高的,抽象度越高的使用越簡單,也是Apple最推薦使用的。可是就目前而言,iOS的開發者,須要瞭解三種多線程技術的基本使⽤用過程。由於不少 框架技術分別使用了不一樣多線程技術。
##四、NSThread
建立線程、執行下載任務,須要start手動開啓執行
NSThread *thread_A = [[NSThread alloc] initWithTarget:self selector:@selector(run_A) object:nil]; thread_A.name = @"線程A";
啓動任務
[thread_A start];
阻塞線程,等待幾秒,是這個線程裏面的全部任務一塊兒阻塞
[NSThread sleepForTimeInterval:5];
阻塞到某個時間
[NSThread sleepUntilDate:[[NSDate date] dateByAddingTimeInterval:1000]];
方式二:指定任務直接執行不須要手動開啓 start
[NSThread detachNewThreadSelector:@selector(run_A) toTarget:self withObject:nil];
取消進程
[thread_A cancel];
獲取當前線程信息
+ (NSThread *)currentThread;
獲取主線程信息
+ (NSThread *)mainThread;
ps:若是是有關UI更新的操做,在其它線程中處理完以後要回到主線程進行改變
##五、GCD
(GCD)是Apple開發的⼀個多核編程的解決⽅方法。該⽅方法在Mac OS X 10.6首次推出,並隨後被引入到了iOS4.0中。GCD是⼀個替代諸如NSThread, NSOperationQueue, NSInvocationOperation等技術的很高效和強大的技術。
###5.1 任務和隊列
在GCD中,加入了兩個很是重要的概念:任務和隊列。
任務:
即操做,你想要幹什麼,說白了就是一段代碼,在 GCD 中就是一個 Block,因此添加任務十分方便。
任務有兩種執行方式: 同步執行 和 異步執行,
同步(sync) 和 異步(async) 的主要區別在於會不會阻塞當前線程,直到 Block 中的任務執行完畢!
若是是 同步(sync) 操做,它會阻塞當前線程並等待 Block 中的任務執行完畢,而後當前線程纔會繼續往下運行。
若是是 異步(async)操做,當前線程會直接往下執行,它不會阻塞當前線程。
隊列:
DISPATCH_QUEUE_SERIAL
和 並行隊列DISPATCH_QUEUE_CONCURRENT
.放到串行隊列的任務,GCD 會 FIFO(先進先出) 地取出來一個,執行一個,而後取下一個,這樣一個一個的執行。
放到並行隊列的任務,GCD 也會 FIFO的取出來,但不一樣的是,它取出來一個就會放到別的線程,而後再取出來一個又放到另外一個的線程。這樣因爲取的動做很快,忽略不計,看起來,全部的任務都是一塊兒執行的。不過須要注意,GCD 會根據系統資源控制並行的數量,因此若是任務不少,它並不會讓全部任務同時執行。
並行隊列 中的任務根據同步或異步有不一樣的執行方式。請看下錶:
主隊列 | 全局隊列 | 自定義串行隊列 | 自定義並行隊列 | |
---|---|---|---|---|
同步 | 死鎖(一直卡主) | 在主線程中順序執行 | 在主線程中順序執行 | 在主線程中順序執行 |
異步 | 在主線程中順序執行 | 在其它線程中同時執行 | 在其它線程中順序執行 | 在其它線程中同時執行 |
###5.2 建立隊列
這是一個特殊的串行隊列。它用於刷新UI,任何須要刷新UI的工做都要在主隊列執行,因此通常耗時的任務都要放到別的線程執行。
同步任務:dispatch_sync,會阻塞後面的任務,必需當前任務完成後才能執行下一個
主隊列中不能執行同步操做,否則會死鎖
異步執行:dispatch_async,不會阻塞後面的任務,任務會立刻返回下一個任務不須要等待,當這個任務完成後會通知主線程。 dispatch_get_main_queue()
獲取主隊列,主隊列中的任務都會在主線程中執行, 是一個串行隊列
dispatch_async(dispatch_get_main_queue(), ^{ [self run_D]; });
dispatch_get_global_queue()
全局隊列,是一個並行隊列,能夠將隊列中的任務放到不一樣的線程中執行
任務執行的優先級 DISPATCH_QUEUE_PRIORITY_HIGH 2 最高 DISPATCH_QUEUE_PRIORITY_DEFAULT 0 中等(默認) DISPATCH_QUEUE_PRIORITY_LOW (-2) 低 DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 後臺執行 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self run_A]; });
若是在並行隊列中同步執行任務,那麼這些任務都會在主線程中按順序執行,也就沒有併發性了。
自定義的串行隊列中異步執行任務,隊列會把任務放到一個新的線程中按順序執行
dispatch_queue_t serialQueue = dispatch_queue_create("串行隊列", DISPATCH_QUEUE_SERIAL); dispatch_async(serialQueue, ^{ [self run_A]; });
自定義的並行隊列中異步執行任務,隊列會把任務放到不一樣的線程中執行
ispatch_queue_t concurrentlQueue = dispatch_queue_create("並行隊列", DISPATCH_QUEUE_CONCURRENT); dispatch_sync(concurrentlQueue, ^{ [self run_A]; });
將更新UI的操做放到主隊列中
dispatch_async(dispatch_get_main_queue(), ^{ self.imagView_1.image = image; });
###5.3 隊列組
隊列組能夠將不少隊列添加到一個組裏,這樣作的好處是,當這個組裏全部的任務都執行完了,隊列組會經過一個方法通知咱們。
建立一個隊列組
dispatch_group_t group = dispatch_group_create();
建立隊列
dispatch_queue_t queue = dispatch_queue_create("並行隊列", DISPATCH_QUEUE_CONCURRENT); dispatch_queue_t global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
在group中異步執行任務,block中調用局部變量須要加__block
__block UIImage *image_1; dispatch_group_async(group, queue, ^{ image_1 = [self downloadImage_one]; });
當group中的任務都執行完後,就會發送一個通知notify調用這個方法
dispatch_group_notify(group, dispatch_get_main_queue(), ^{ self.imagView_1.image = image_1; });
###5.4 線程安全
一次執行,block中的代碼只能被執行一次,是線程保護的(同時只能一個線程訪問)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self run_B]; });
單例通常用這個 線程安全的單例建立
+ (instancetype)shareUser_GCD { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ user = [[self alloc] init]; }); return user; } + (instancetype)allocWithZone:(struct _NSZone *)zone { static User *user = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ user = [super allocWithZone:zone]; }); return user; }
延遲執行
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self run_B]; });
##六、NSOperation和NSOperationQueue
NSOperation 是蘋果公司對 GCD 的封裝,徹底面向對象,因此使用起來更好理解。 NSOperation 和 NSOperationQueue 分別對應 GCD 的 任務 和 隊列 。操做步驟也很好理解:
1.將要執行的任務封裝到一個 NSOperation 對象中。 2.將此任務添加到一個 NSOperationQueue 對象中。
直接執行會在主線程中順序執行
NSInvocationOperation *opertaion_1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run_A) object:nil];
啓動任務
[opertaion_1 start];
至少會有一個任務在主線程中執行,其餘任務會被放到其餘線程中執行
NSBlockOperation *opertion = [NSBlockOperation blockOperationWithBlock:^{ [self run_A]; }]; [opertion addExecutionBlock:^{ [self run_B]; }]; [opertion addExecutionBlock:^{ [self run_C]; }]; [opertion start];
###6.1 NSInvocationOperation依賴關係
NSOperation 有一個很是實用的功能,那就是添加依賴。好比有 3 個任務:A: 從服務器上下載一張圖片,B:給這張圖片加個水印,C:把圖片返回給服務器。這時就能夠用到依賴
注意:
NSInvocationOperation *opertaion_1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run_A) object:nil]; NSInvocationOperation *opertaion_2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run_B) object:nil]; NSInvocationOperation *opertaion_3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run_C) object:nil]; // 添加依賴,opertaion_1 ————》opertaion_2 ————》opertaion_3 // [opertaion_2 addDependency:opertaion_1]; // [opertaion_3 addDependency:opertaion_2];
在這裏就是opertaion_2會等opertaion_1執行完後再執行,後面以此類推
建立一個隊列,默認就是並行隊列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
設置當前最大的執行數,能夠控制是並行還串行,爲1時就是串行
queue.maxConcurrentOperationCount = 1;
將任務添加到隊列中任務就自動執行了
[queue addOperation:opertaion_1]; [queue addOperation:opertaion_2]; [queue addOperation:opertaion_3]; [queue addOperationWithBlock:^{ [self run_D]; }];
添加多個任務,而且能夠設置等待完成
[queue addOperations:@[opertaion_1,opertaion_2,opertaion_3] waitUntilFinished:YES];
取消全部的任務
[queue cancelAllOperations];
取消指定的任務
[opertaion_1 cancel];
獲取當前的隊列
[NSOperationQueue currentQueue];
獲取主隊列
[NSOperationQueue mainQueue];
###6.2 其它方法
以上就是一些主要方法, 下面還有一些經常使用方法:
NSOperation
BOOL executing;
//判斷任務是否正在執行BOOL finished;
//判斷任務是否完成void (^completionBlock)(void);
//用來設置完成後須要執行的操做(void)cancel;
//取消任務(void)waitUntilFinished;
//阻塞當前線程直到此任務執行完 畢NSOperationQueue
NSUInteger operationCount;
//獲取隊列的任務數(void)cancelAllOperations;
//取消隊列中全部的任務(void)waitUntilAllOperationsAreFinished;
//阻塞當前線程直到此隊列中的全部任務執行完畢[queue setSuspended:YES];
// 暫停queue[queue setSuspended:NO];
// 繼續queue##7 GCD和NSOperation的區別:
一、GCD的底層是用C來實現的,NSOperation底層從ios4開始也是⽤的GCD來實 現的;(底層實現)
二、在NSOperationQueue中,咱們能夠隨時取消已經設定要準備執⾏的任務(固然,已經開始的任務就⽆法阻⽌了),⽽GCD無法中止已經加入queue的block(實際上是有的,但須要許多複雜的代碼);(取消任務)
三、NSOperation可以⽅便地設置依賴關係,咱們可讓⼀個Operation依賴於另⼀個Operation,這樣的話儘管兩個Operation處於同⼀個並行隊列中,但前者會直到後者執行完畢後再執⾏;(依賴關係)
四、咱們能將KVO應⽤用在NSOperation中,能夠監聽一個Operation是否完成或取消,這樣子能⽐GCD更加有效地掌控咱們執⾏的後臺任務;(監放任務的執⾏狀況)
五、在NSOperation中,咱們可以設置NSOperation的priority優先級,可以使同⼀個並行隊列中的任務區分前後地執行,⽽在GCD中,咱們只能區分不一樣任務隊列的優先級,若是要區分block任務的優先級,也須要大量的複雜代碼;(優先級) 六、咱們可以對NSOperation進行繼承,在這之上添加成員變量與成員方法,提⾼整個代碼的復⽤度,這⽐簡單地將block任務排⼊執行隊列更有自由度,可以在其之上添加更多自定製的功能。(代碼複用)
Demo下載: