多線程1
- 進程:
- 概念:系統中正在運行的一個應用程序
- 特點:每個進程都是獨立的,運行在自己獨有的且受保護的內存空間中。
- 線程:
- 概念:執行進程的單位,依賴于進程而存在
- NSThread創建線程的四種方法:
方法1
NSThread *threadC = [[NSThread alloc]initWithTarget:self selector:@selector(run:) object:@"創建線程1"];
方法2
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"分離一條子線程"];
方法3
[self performSelectorInBackground:@selector(run:) withObject:@"創建后臺線程"];
方法4(自定義)
[[[NSThread alloc]init]start];
- 進程和線程的比較
- 進程是cpu分配資源和調度的單位
- 線程是cpu進行調度的最小單元
- 一個正在運行的應用程序只能有一個進程,一個進程有不少于一個線程
- 同一個進程內的線程共享進程的資源
- 多線程:
- 原理:cpu分時快速在各個線程之間切換調用
- 優缺點:
- 優點:提高程序的執行效率以及內存和cpu的利用率
- 缺點:開線程會耗費資源,尤其是大量開線程的情況,并且會是程序設計更加復雜
- 主線程:
- 概念:程序運行自動開啟的線程(主線程或UI線程)
- 作用:顯示刷新UI界面、處理UI事件
- 注意點: 主線程不宜執行耗時較長的操作
- 線程安全問題小結:
- 售票員實例:
// 創建三個售票員線程,讓三個售票員分時搶占資源 self.totalSize = 100; self.obj = [[NSObject alloc]init]; //創建線程 self.threadA = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil]; [self.threadA setName:@"售票員A"]; self.threadB = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil]; [self.threadB setName:@"售票員B"]; self.threadC = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil]; [self.threadC setName:@"售票員C"]; //開始執行 [self.threadA start]; [self.threadB start]; [self.threadC start]; // 讓售票員分時搶占資源 -(void)saleTicket { while (1) { @synchronized(self) { NSInteger count = self.totalSize; if (count >0) { for (NSInteger i = 0; i<10000000; i++) { //演示耗時操作 } //檢查余票的數量,如果發現有余票就賣出去一張 self.totalSize = count - 1; NSLog(@"%@賣出去了一張,還剩下%zd張票",[NSThread currentThread].name,self.totalSize); }else { NSLog(@"不要回家的,或者做飛機回去吧"); break; } } } }
- 售票員實例:
- 線程間通訊(下載圖片實例):
- 注意圖片設置一定要放到主線程中:
//1.確定url
NSURL *url = [NSURL URLWithString:@"http://img4.duitang.com/uploads/blog/201310/18/20131018213446_smUw4.thumb.700_0.jpeg"];
//2.下載圖片的二進制數據到本地
NSData *data = [NSData dataWithContentsOfURL:url];
//3.轉換格式 二進制數據轉換UIimage
UIImage *image = [UIImage imageWithData:data];
NSLog(@"%@---",[NSThread currentThread]);
//4.回到主線程刷新UI
[self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:NO];
[self performSelector:@selector(showImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES];
[self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];
//獲得的時間是絕對時間
CFTimeInterval start = CFAbsoluteTimeGetCurrent();
- 線程的狀態(創建、開啟、運行、阻塞、死亡):
- 阻塞狀態:
[NSThread sleepForTimeInterval:2.0]; [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:10.0]];
- 強行退出
[NSThread exit]
- GCD的使用(重點):
- 重要概念(隊列是GCD中的概念):
- 并發隊列(并發隊列):允許多個任務同時執行
- 串行隊列(主隊列):多個任務依次執行,其中主隊列必須在主線程中執行,當需要任務調度時首先檢查主線程狀態,若主線程阻塞,則停止調動
- 同步:必須得到該方法的返回值才能往下執行
- 異步:不必得到該方法的返回值依然可以向下執行
- 隊列:(用來存放任務)
- 并發隊列:(允許多個隊列同時執行):
1)直接create dispatch_queue_create
2)全局并發隊列 - 串行隊列:(任務串行執行)
1)直接create dispatch_queue_create
2)主隊列:
1)所有在主隊列中的任務都會被放在主線程中執行
2)主隊列中的任務在執行之前會先檢查主線程的狀態, 如果發現主線程當前正在執行任務那么會暫停隊列中任務的調度- 隊列與函數組合是否開線程以及開幾條線程: - 異步函數+主隊列:不會開線程,所有的任務串行執行
//1.獲得主隊列 dispatch_queue_t queue = dispatch_get_main_queue(); //2.封裝任務 dispatch_async(queue, ^{ NSLog(@"1---%@",[NSThread currentThread]); });
- 同步函數+主隊列:不會開線程,所有任務串行執行
//1.獲得主隊列 dispatch_queue_t queue = dispatch_get_main_queue(); //2.封裝任務 dispatch_sync(queue, ^{ NSLog(@"1---%@",[NSThread currentThread]); });
- 異步函數+串行隊列:開一條線程,串行執行
dispatch_queue_t queue = dispatch_queue_create("com.seemygo.www.download", DISPATCH_QUEUE_SERIAL); dispatch_async(queue, ^{ NSLog(@"1---%@",[NSThread currentThread]); });
- 異步函數+并發隊列:開多條線程,并發執行
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(queue, ^{ NSLog(@"1---%@",[NSThread currentThread]); });
- 同步函數+并發隊列:不會開線程,并發執行
dispatch_queue_t queue = dispatch_queue_create("com.seemygo.www.download", DISPATCH_QUEUE_CONCURRENT); dispatch_sync(queue, ^{ NSLog(@"1---%@",[NSThread currentThread]); });
- 同步函數+串行:不會開線程,串行執行
dispatch_queue_t queue = dispatch_queue_create("com.seemygo.www.download", DISPATCH_QUEUE_SERIAL); dispatch_sync(queue, ^{ NSLog(@"1---%@",[NSThread currentThread]); });
- GCD線程間通信:
//1.開線程下載圖片 //DISPATCH_QUEUE_PRIORITY_DEFAULT 0 dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"%@----",[NSThread currentThread]); //1.1 確定url NSURL *url = [NSURL URLWithString:@"http://img4.duitang.com/uploads/blog/201308/24/20130824215734_ut8LZ.thumb.600_0.jpeg"]; //1.2 下載二進制數據到本地 NSData *data = [NSData dataWithContentsOfURL:url]; //1.3 轉換圖片 UIImage *image = [UIImage imageWithData:data]; //刷新UI dispatch_sync(dispatch_get_main_queue(), ^{ self.imageView.image = image; NSLog(@"%@--UI--",[NSThread currentThread]); }); });
- GCD中常用函數:
- 延時函數
1.方法 [self performSelector:@selector(task) withObject:nil afterDelay:2.0]; 2.方法 [NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(task) userInfo:nil repeats:NO]; 3.方法 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0), ^{ NSLog(@"--GCD----%@",[NSThread currentThread]); });
- 一次性函數
// 整個應用程序只會執行一次,并且是線程安全的 static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSLog(@"--once---%@",[NSThread currentThread]); });
- 柵欄函數
1.在子線程中,確保前面任務執行完才會執行后面線程 dispatch_barrier_async(queue, ^{ NSLog(@"+++++++++++++%@",[NSThread currentThread]); });
- 快速迭代函數
//注意:主線程也會參與迭代的過程,里面的任務是并發執行的 !!!!不能傳主隊列 dispatch_apply(10, queue, ^(size_t index) { NSLog(@"%zd---%@",index,[NSThread currentThread]); });