目錄html
進程是指在系統中正在運行的一個應用程序。每一個進程之間是獨立的,每一個進程均運行在其專用且受保護的內存空間內。ios
基本概念:一個進程要想執行任務,必須得有線程(每個進程至少要有一條線程),線程是進程的基本執行單元,一個進程(程序)的全部任務都在線程中執行。
線程的串行:一條線程中任務的執行是串行的,若是要在一條線程中執行多個任務,那麼只能一個一個地按順序執行這些任務。也就是說,在同一時間內,一條線程只能執行一個任務。編程
基本概念:即一個進程中能夠開啓多個線程,每條線程能夠並行(同時)執行不一樣的任務。
線程的並行:並行即同時執行。好比同時開啓3條線程分別下載3個文件(分別是文件A、文件B、文件C)。
多線程併發執行的原理:在同一時間裏,CPU只能處理1條線程,只有1條線程在工做(執行)。多線程併發(同時)執行,實際上是CPU快速地在多條線程之間調度(切換),若是CPU調度線程的時間足夠快,就形成了多線程併發執行的假象。
多線程優缺點
優勢:能適當提升程序的執行效率、能適當提升資源利用率(CPU、內存利用率)
缺點:
1)開啓線程須要佔用必定的內存空間(默認狀況下,主線程佔用1M,子線程佔用512KB),若是開啓大量的線程,會佔用大量的內存空間,下降程序的性能。
2)線程越多,CPU在調度線程上的開銷就越大。
3)程序設計更加複雜:好比線程之間的通訊、多線程的數據共享安全
1)一個iOS程序運行後,默認會開啓1條線程,稱爲「主線程」或「UI線程」。
2)做用。刷新顯示UI,處理UI事件。多線程
1)不要將耗時操做放到主線程中去處理,會卡住線程。
2)和UI相關的刷新操做必須放到主線程中進行處理。
併發
技術方案 | 簡介 | 語言 | 線程生命週期管理 | 使用頻率 |
---|---|---|---|---|
pthread | —> 一套通用的多線程API —> 適用於Unix\Linux Windows等系統 —> 跨平臺\可植入 —> 使用難度比較大 |
C | 手動 | 極低 |
NSThread | —> 使用更加面向對象 —> 簡單易用,能夠直接操做線程對象 |
Objective-C | 手動 | 底 |
GCD | —> 旨在替代NSThread等線程技術 —> 充分利用設備的多核 |
C | 自動 | 高 |
NSOperation | —> 基於GCD (底層是GCD) —> 比GCD多了一些更簡單實用的功能 —> 使用更加面向對象 |
Objective-C | 自動 | 高 |
//pthread須要包含頭文件 //1.建立線程對象 pthread_t thread; NSString *name = @"線程"; //2.使用pthread建立線程 //第一個參數:線程對象地址 //第二個參數:線程屬性 //第三個參數:指向函數的指針 //第四個參數:傳遞給該函數的參數 pthread_create(&thread, NULL, run, (__bridge void *)(name));
// 第一種:alloc init,須要手動開啓線程,能夠拿到線程對象進行詳細設置 NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(run:) object:@"線程"]; [thread start]; // 第二種:分離(detach)出一條子線程,自動啓動線程,沒法對線程進行更詳細的設置 [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"分離出的子線程"]; // 第三種:後臺線程,自啓動,不能詳細設置 [self performSelectorInBackground:@selector(run:) withObject:@"後臺線程"];
thread.name = @"線程A"; // 線程名稱 thread.threadPriority = 1.0; // 線程的優先級,取值範圍0.0~1.0,1.0的優先級最高,默認0.5
// 線程的各類狀態:新建-就緒-運行-阻塞-死亡 // 經常使用的控制線程狀態的方法 [NSThread exit]; // 退出當前線程 [NSThread sleepForTimeInterval:2.0]; // 阻塞線程 [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];//阻塞線程 // 注意:線程死了不能復生
01 前提:多個線程訪問同一塊資源會發生數據安全問題 02 解決方案:加互斥鎖 03 相關代碼:@synchronized(self){} 04 專業術語-線程同步 05 原子和非原子屬性(是否對setter方法加鎖)
-(void)touchesBegan:(nonnull NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event { // 開啓子線程下載圖片 [NSThread detachNewThreadSelector:@selector(downloadImage) toTarget:self withObject:nil]; } -(void)downloadImage { NSURL *url = [NSURL URLWithString:@"http://p6.qhimg.com/t01d2954e2799c461ab.jpg"]; UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]]; // 回到主線程刷新UI,如下三種任選其一,第二種方面些 [self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:YES]; [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES]; [self.imageView performSelector:@selector(setImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES]; }
//第一種方法 NSDate *start = [NSDate date]; NSData *data = [NSData dataWithContentsOfURL:url]; NSDate *end = [NSDate date]; NSLog(@"耗時%f",[end timeIntervalSinceDate:start]); //第二種方法 CFTimeInterval start = CFAbsoluteTimeGetCurrent(); NSData *data = [NSData dataWithContentsOfURL:url]; CFTimeInterval end = CFAbsoluteTimeGetCurrent(); NSLog(@"耗時%f",end - start);
主隊列:dispatch_get_main_queue()app
併發隊列:dispatch_get_global_queue(0, 0),dispatch_queue_create("com.test", DISPATCH_QUEUE_CONCURRENT)異步
手動建立串行隊列:dispatch_queue_create("com.test", DISPATCH_QUEUE_SERIAL)async
併發隊列 | 手動建立的串行隊列 | 主隊列 | |
---|---|---|---|
dispatch_sync | 沒有開啓新的線程,串行執行 | 同← | 同← |
dispatch_async | 有開啓新的線程,併發執行 | 有開啓新的線程,串行執行 | 沒有開啓新的線程,串行執行 |
// 獲取一個全局的隊列 dispatch_queue_t queue = dispatch_get_global_queue(0, 0); // 先開啓一個線程,把下載圖片的操做放在子線程中處理 dispatch_async(queue, ^{ // 下載圖片 NSString *urlString = @"http://h.hiphotos.baidu.com/zhidao/pic/item/6a63f6246b600c3320b14bb3184c510fd8f9a185.jpg"; NSURL *url = [NSURL URLWithString:urlString]; UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]]; // 回到主線程刷新UI dispatch_async(dispatch_get_main_queue(), ^{ self.imageView.image = image; }); });
—> 柵欄函數(控制任務的執行順序)函數
如下實例,會保證Task One和Task Two所有完成後執行以後的Task Three和Task Four
dispatch_queue_t queue = dispatch_queue_create("com.example.myqueue", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ for (int i = 0; i < 10; i++) { NSLog(@"Task One"); } }); dispatch_async(queue, ^{ for (int i = 0; i < 10; i++) { NSLog(@"Task Two"); } }); // The queue you specify should be a concurrent queue that you create yourself using the dispatch_queue_create function. If the queue you pass to this function is a serial queue or one of the global concurrent queues, this function behaves like the dispatch_async function. // 使用 dispatch_queue_create 函數建立併發隊列(使用DISPATCH_QUEUE_CONCURRENT, 而不是DISPATCH_QUEUE_SERIAL), 不要使用dispatch_get_global_queue, 不然和dispatch_async同樣效果 dispatch_barrier_async(queue, ^{ NSLog(@"++++++++++"); }); dispatch_async(queue, ^{ for (int i = 0; i < 10; i++) { NSLog(@"Task Three"); } }); dispatch_async(queue, ^{ for (int i = 0; i < 10; i++) { NSLog(@"Task Four"); } });
—> 延遲執行(延遲而且能夠控制在哪一個線程執行)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ // code to be executed after a specified delay });
—> 一次性代碼
-(void)once { // 整個程序運行過程當中只會執行一次(注意區分一次性代碼和懶加載) // onceToken用來記錄該部分的代碼是否被執行過 static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ }); }
—> 快速迭代(開多個線程併發完成迭代操做)
dispatch_apply(12, dispatch_get_global_queue(0, 0), ^(size_t index) { NSLog(@"SuperLog------ %zd",index); });
—> 隊列組(同柵欄函數)
dispatch_queue_t queue = dispatch_queue_create("com.example", DISPATCH_QUEUE_CONCURRENT); dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, queue, ^{ }); // 隊列組中的任務執行完畢以後,執行該函數 dispatch_group_notify(group, queue, ^{ });
—> NSInvocationOperation
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test) object:nil]; [operation start];
—> NSBlockOperation
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ // 在主線程中執行 NSLog(@"downloadOne--%@",[NSThread currentThread]); }]; // 增長操做,增長的操做在子線程中執行 [operation addExecutionBlock:^{ NSLog(@"downloadTwo--%@",[NSThread currentThread]); }]; [operation addExecutionBlock:^{ NSLog(@"downloadThree--%@",[NSThread currentThread]); }]; [operation start];
—> 自定義NSOperation
// 自定義的NSOperation, 經過重寫內部的main方法實現封裝操做 -(void)main { NSLog(@"--main--%@",[NSThread currentThread]); } // 使用 SPOperation *operation = [[SPOperation alloc] init]; [operation start];
GCD中的隊列
串行隊列:dispatch_queue_create("com.test", DISPATCH_QUEUE_SERIAL),dispatch_get_main_queue()
併發隊列:dispatch_queue_create("com.test", DISPATCH_QUEUE_CONCURRENT),dispatch_get_global_queue(0, 0)NSOperationQueue
主隊列:[NSOperationQueue mainqueue] 放在主隊列中的操做都在主線程中執行
非主隊列:[[NSOperationQueue alloc] init] 併發和串行,默認是併發執行的
// 自定義NSOperation -(void)customOperation { // 建立隊列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 建立操做 SPOperation *operationOne = [[SPOperation alloc] init]; SPOperation *operationTwo = [[SPOperation alloc] init]; // 添加操做到隊列中 [queue addOperation:operationOne]; [queue addOperation:operationTwo]; // You can call this method explicitly if you want to execute your operations manually. However, it is a programmer error to call this method on an operation object that is already in an operation queue or to queue the operation after calling this method. Once you add an operation object to a queue, the queue assumes all responsibility for it. // 有隊列管理,不要手動調用 start // [operationOne start] } //NSBlockOperation - (void)block { // 建立隊列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 封裝操做 NSBlockOperation *operationOne = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"operationOne--%@",[NSThread currentThread]); }]; NSBlockOperation *operationTwo = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"operationTwo--%@",[NSThread currentThread]); }]; [operationTwo addExecutionBlock:^{ NSLog(@"operationThree--%@",[NSThread currentThread]); }]; // 添加操做到隊列中 [queue addOperation:operationOne]; [queue addOperation:operationTwo]; // 開發直接使用該方法便可,簡單方便 [queue addOperationWithBlock:^{ NSLog(@"operationFour--%@",[NSThread currentThread]); }]; } // NSInvocationOperation - (void)invocation { // 建立隊列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 封裝操做 NSInvocationOperation *operationOne = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(downloadOne) object:nil]; NSInvocationOperation *operationTwo = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(downloadTwo) object:nil]; // 把封裝好的操做添加到隊列中 [queue addOperation:operationOne]; [queue addOperation:operationTwo]; }
—> 設置最大併發數
NSOperationQueue *queue = [[NSOperationQueue alloc]init]; // 設置最大併發數, 該屬性須要在任務添加到隊列中以前進行設置 // 該屬性控制隊列是串行執行仍是併發執行, 1 串行 >1 並行 // 系統的最大併發數有個默認的值,爲-1,若是該屬性設置爲0,那麼不會執行任何任務 queue.maxConcurrentOperationCount = 2;
—> 暫停和恢復以及取消
//設置暫停和恢復 //暫停表示不繼續執行隊列中的下一個任務,暫停操做是能夠恢復的 if (self.queue.isSuspended) { self.queue.suspended = NO; }else { self.queue.suspended = YES; } // 取消隊列裏面的全部操做 // 取消以後,當前正在執行的操做的下一個操做將再也不執行,並且永遠都不在執行,就像後面的全部任務都從隊列裏面移除了同樣 // 取消操做是不能夠恢復的 [self.queue cancelAllOperations]; // 自定義NSOperation取消操做 -(void)main { //耗時操做 for (int i = 0; i<1000; i++) { NSLog(@"任務1-%d--%@",i,[NSThread currentThread]); } NSLog(@"+++++++++++++++++++++++++++++++++"); //蘋果官方建議,每當執行完一次耗時操做以後,就查看一下當前隊列是否爲取消狀態,若是是,那麼就直接退出(爲了提升程序的性能) if (self.isCancelled) { return; } }
—> 開子線程下載圖片
NSOperationQueue *queue = [[NSOperationQueue alloc]init]; [queue addOperationWithBlock:^{ NSURL *url = [NSURL URLWithString:@"http://h.hiphotos.baidu.com/zhidao/pic/item/6a63f6246b600c3320b14bb3184c510fd8f9a185.jpg"]; UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]]; // 主線程刷新UI [[NSOperationQueue mainQueue] addOperationWithBlock:^{ self.imageView.image = image; }]; }];
—> 操做依賴(operationThree 依賴operationOne和operationTwo)
NSOperationQueue *queue = [[NSOperationQueue alloc]init]; NSBlockOperation *operationOne = [NSBlockOperation blockOperationWithBlock:^{ // Task One }]; NSBlockOperation *operationTwo = [NSBlockOperation blockOperationWithBlock:^{ // Task Two }]; NSBlockOperation *operationThree = [NSBlockOperation blockOperationWithBlock:^{ // Task Three }]; // 操做依賴 [operationThree addDependency:operationOne]; [operationThree addDependency:operationTwo]; // 添加操做到隊列中執行 [queue addOperation:operationOne]; [queue addOperation:operationTwo]; [queue addOperation:operationThree];
使用create函數建立的併發隊列和全局併發隊列的主要區別: