iOS学习之多线程
在iOS中提供了4中多線程的方式
以下4中方式:
pthread使用較少
? ? ?NSThread *current = [NSThread currentThread];
1、創建一個線程id
pthread_t threadId; pthrad_create(&threadId,NULL,run,NULL);
void *run(void *data) { ? ? ?return NULL; }
NSThread 一個NSThread對象就代表一條線程 使用更加面向對象,可以直接操作線程對象,oc 語言 ? 程序員進行管理 線程的生命周期
主線程相關用法 NSThread *current = [NSThread currentThread]; NSThread *mainThread = [NS] 判斷是不是主線程 [NSThread isMainThread];
線程的優先級 NSThread?threadPriority -(BOOL)setThreadPriority:(double); 調度優先級的取值范圍是0.0 — 1.0 值越大 優先級越高
1、創建線程 *1 NSThread *thread = [[ NSThread alloc ] initWithTarget : self? selector : @selector (run:) object : @"aaa" ]; // 開啟線程 [thread start];
*2 創建后就執行 ?自動啟動 無法對線程進行更詳細的設置 [NSThread detachNewThreadS elector : @selector (run:) object : @"aaa" ]; ]
3*// 隱式創建 [self performSelectorInBackground:@selector(run:)withObject:@"aa"];
-(void)run { ?????
}
線程的生命周期 新建——————》線程對象進入可調度線程池(就緒狀態)——————》運行狀態(如果cpu調度當前線程)——————》調用sleep方法?當前線程從調度池 ,但是該線程仍然在內存中?—————》當阻塞結束 當前進程進入就緒狀態
線程任務執行完畢、異常退出、強制退出 ?都是 線程進入死亡狀態(仍然在內存中) //阻塞線程 +(void)sleepUntiData:(NSDate:)date; + ? (void)sleepForTimeInterval:(NSTimeInterval)ti; 強制停止線程 +(void)exit; ? 線程進入死亡狀態
線程加鎖(線程同步)—》多條線程按順序的執行任務 ?互斥鎖 就是使用了線程同步技術 NSLock *lock = [[NSLock alloc]init]; 線程讀之前加鎖(只能用一把鎖)
? @synchronized(self){ 加鎖 ? ? ?大括號內的代碼有線程安全問題代碼要進行加鎖操作
}大括號結束解鎖
線程間通信 兩個進行線程間通信的方法 下面方法在主線程中執行 settingImage 方法 [self performSelectorOnMainThread:@selector(settingImage:) withObject:image waitUntilDone:NO];
下面方法是在 onThread指定的線程中執行某個方法 [self performSelector:<#(SEL)#> onThread:<#(NSThread *)#> withObject:<#(id)#> waitUntilDone:<#(BOOL)#> modes:<#(NSArray *)#>]
對ui界面的操作要在主線程中執行,例如以下載圖片為例可以使用 下面方法的意思是:在主線程中調用imageView 的 setImage:方法將imageView的image設置為 下載好的image 其中 waitUntilDone設置為NO為 調用完這個方法會立即返回? ? ?waitUntilDone設置為YES 這個方法會在主線程執行完setImage:方法后返回 [self.imageView performSelector:@selector(setImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:NO];
[ self . imageView performSelectorOnMainThread : @selector (setImage:) withObject :image waitUntilDone : NO ];
GCD???? Grand Central Dispatch? 牛逼的中樞調度器
優勢: 1、多核的并行運算提出的解決方案 2、會自動利用更多的cpu內核 3、自動關了線程的生命周期(創建線程、調度任務、銷毀線程) 4、不需要編寫任何線程管理代碼 ? 只需要告訴GCD 要執行的任務 ?GCD 會自動創建隊列執行任務
使用:兩個步驟 ? 定制任務 ? 添加定制任務到隊列中 —》 GCD會自動將隊列中的任務取出,放到對應的線程中執行 任務的取出遵循 FIFO ?先進先出
//凡是函數名種帶有create\copy\new\retain等字眼,都需要在不需要使用這個數據的時候進行release
// GCD的數據類型在ARC環境下不需要再做release // CF(Core Foundation) 的數據類型在 ARC 環境下還是需要再做 release
/**
?*? 全局并發隊列
?*/
#define global_queue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 )
/**
?*? 主隊列
?*/ #define main_queue dispatch_get_main_queue()
基本使用:(同步函數和異步函數———決定了要不要開啟新的線程) 1、2個用來執行任務的函數 同步:在當前線程中執行任務 不具備開啟新線程的能力 dispatch_sync(<#dispatch_queue_t queue#>, <#^(void)block#>)
異步:在另一條線程中執行 ?具備了開啟新線程的能力 dispatch_async(<#dispatch_queue_t queue#>, <#^(void)block#>)
特殊情況:異步函數的任務添加在主隊列中(往主隊列中添加一個同步任務), 任務在主線程執行,不會開啟新的線程
2、隊列 分為兩大類(并發和串行————決定了任務的執行方式) 并發隊列:可以讓多個任務并發同時執行(自動開啟多條線程) ? 在異步函數中執行
串行隊列:讓任務一個接著一個執行
并發隊列:GCD默認已經提供了全局的并發隊列供整應用程序使用,不需要手動創建
獲取一個全局的默認優先級的并發隊列 dispatch_queue_tqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
添加任務到隊列中執行任務
1、 用 dispatch_async 異步函數往并發隊列中添加任務—————》同時開啟了3個子線程 - (void)asyncGlobalQueue
{ ???//?獲取一個全局的默認優先級的并發隊列 ???dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
???
??? // 2.添加任務到隊列中執行
??? dispatch_async(queue, ^{
??????? NSLog(@"----下載圖片1-----%@", [NSThreadcurrentThread]);
??? });
??? dispatch_async(queue, ^{
??????? NSLog(@"----下載圖片2-----%@", [NSThreadcurrentThread]);
??? });
??? dispatch_async(queue, ^{
??????? NSLog(@"----下載圖片3-----%@", [NSThreadcurrentThread]); ??? }); ???// 總結:同時開啟了3個線程 }
2、用dispatch_async異步函數 往串行隊列中添加任務————————》只開了一個線程執行任務 - (void)asyncSerialQueue {
??? // 1.創建串行隊列
??? dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue",NULL);
???
??? // 2.添加任務到隊列中執行
??? dispatch_async(queue, ^{
??????? NSLog(@"----下載圖片1-----%@", [NSThreadcurrentThread]);
??? });
??? dispatch_async(queue, ^{
??????? NSLog(@"----下載圖片2-----%@", [NSThreadcurrentThread]);
??? });
??? dispatch_async(queue, ^{
??????? NSLog(@"----下載圖片3-----%@", [NSThreadcurrentThread]);
??? }); ? ?//總結:只開1個線程執行任務 }
3、用dispatch_sync同步函數往并發隊列中添加任務—————》不會開啟新的線程,并發隊列失去了并發的功能 - (void)syncGlobalQueue
{
??? // 1.獲得全局的并發隊列
??? dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
???
??? // 2.添加任務到隊列中執行
??? dispatch_sync(queue, ^{
??????? NSLog(@"----下載圖片1-----%@", [NSThreadcurrentThread]);
??? });
??? dispatch_sync(queue, ^{
??????? NSLog(@"----下載圖片2-----%@", [NSThreadcurrentThread]);
??? });
??? dispatch_sync(queue, ^{
??????? NSLog(@"----下載圖片3-----%@", [NSThreadcurrentThread]);
??? });
???
??? // 總結:不會開啟新的線程,并發隊列失去了并發的功能 }
4、 用 dispatch_sync 同步函數往串行列中添加任務—————》不會開啟新的線程 - (void)syncSerialQueue
{ ???// 1.創建串行隊列(串行隊列) ???dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue",NULL);
???
??? // 2.添加任務到隊列中執行
??? dispatch_sync(queue, ^{
??????? NSLog(@"----下載圖片1-----%@", [NSThreadcurrentThread]);
??? });
??? dispatch_sync(queue, ^{
??????? NSLog(@"----下載圖片2-----%@", [NSThreadcurrentThread]);
??? });
??? dispatch_sync(queue, ^{
??????? NSLog(@"----下載圖片3-----%@", [NSThreadcurrentThread]);
??? });
???
??? // 3.釋放資源
//??? dispatch_release(queue); ? // MRC(非ARC)
???
??? // 總結:不會開啟新的線程 }
5、 用 dispatch_sync 同步函數 , 在主線程中往主隊列中添加任務 : 任務無法往下執行———————>產生死鎖(死循環) - (void)syncMainQueue
{
??? // 1.獲得主隊列
??? dispatch_queue_t queue = dispatch_get_main_queue();
??? ???// 2.添加任務到隊列中執行 ?????(queue, ^{ ???????NSLog(@"----下載圖片1-----%@", [NSThreadcurrentThread]);
??? });
//??? dispatch_sync(queue, ^{
//??????? NSLog(@"----下載圖片2-----%@", [NSThread currentThread]);
//??? });
//??? dispatch_sync(queue, ^{
//??????? NSLog(@"----下載圖片3-----%@", [NSThread currentThread]);
//??? });
}
6、 使用dispatch_async異步函數,在主線程中往主隊列中添加任務 特殊情況:異步函數的任務添加在主隊列中(往主隊列中添加一個同步任務), 任務在主線程執行,不會開啟新的線程
- ( void )asyncMainQueue { ??? // 1.獲得主隊列(串行隊列) ??? dispatch_queue_t queue = dispatch_get_main_queue ();
???
??? // 2. 添加任務到隊列中 執行
??? dispatch_async (queue, ^{
??????? NSLog ( @"---- 下載圖片 1-----%@" , [ NSThread currentThread ]);
??? }); }
同步函數(sync) 并發隊列:不會開啟線程 串行隊列:不會開啟線程 異步函數(async) 并發隊列:開啟多個線程 串行隊列:開啟一條線程
使用GCD進行異步圖片下載 1、獲取全局并發隊列 ??? dispatch_queue_tqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0); 2、調用異步(同步)函數下載圖片 dispatch_async(queue, ^{//開啟異步線程下載圖片 ???????NSLog(@"當前線程%@",[NSThreadcurrentThread]);
??????? NSURL *url = [NSURLURLWithString:@"http://www.res.meizu.com/resources/www_image/weixin.jpg"];
??????? NSData *data = [NSDatadataWithContentsOfURL:url];
???????
??????? UIImage *image = [UIImageimageWithData:data]; ? ? ?? 3、回到主隊列刷新圖片 ???????dispatch_async(dispatch_get_main_queue(), ^{
??????????? self.imageView.image= image;
??????? }); ??? });
GCD的其他用法
1、延遲執行? *1 ?[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
*2 利用GCD 進行延遲執行(推薦) dispatch_queue_t queue =? dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //??? /**
// ??? *? 2 秒鐘后 ? 在隊列 queue 中執行 block 中的任務
// ??? */
//??? dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), queue, ^{
//??? //??? });
2、一次性代碼 ? 使某一段代碼在整個程序運行過程中只執行一次,用于創建單? dispatch_once static id _instance; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ ??????_instance = [[self alloc]init]; });
3、隊列組 ?當程序需要執行完多個耗時操作后 在執行某個其他操作的時候使用 如:下載兩張圖片 ?下載完成后進行拼接顯示在imageView上 *1、?創建一個組 dispatch_group_t group = dispatch_group_create(); *2、開啟一個任務下載圖片1 __block? UIImage *image1 = nil;
??? //開啟一個任務下載圖片1
??? dispatch_group_async(group, global_queue, ^{
??????? image1 = [self imageWithUrl:@"1"]; ??? }); *3?開啟一個任務下載圖片2 ?? __block UIImage *image2 = nil;
??? //開啟一個任務下載圖片2 ??? dispatch_group_async(group, global_queue, ^{ ? ? image2 = [self imageWithUrl:@"2"]; ??? }); 4* 等待group中的所有任務都執行完畢后 ?在執行其他操作 ??? dispatch_group_notify(group, main_queue, ^{ // 合并圖 ?????UIGraphicsBeginImageContextWithOptions(CGSizeMake(200, 100), NO, 0.0); ?????[image1 drawInRect:CGRectMake(0, 0, 100, 100)]; ?????[image2 drawInRect:CGRectMake(100, 0, 100, 100)]; ?????UIImageView *imageView = nil; ?????imageView.image = UIGraphicsGetImageFromCurrentImageContext(); ?? ??? });
NSOperation
使用NSOperation 的子類 創建任務 ? NSInvocationOperation 、?NSBlockOperation
1、封裝任務 ???NSInvocationOperation *opration1 = [[NSInvocationOperationalloc]initWithTarget:selfselector:@selector(download)object:nil];
???NSInvocationOperation *opration2 = [[NSInvocationOperationalloc]initWithTarget:selfselector:@selector(run)object:nil];
??? NSBlockOperation *blockOperation = [ NSBlockOperation blockOperationWithBlock :^{
?????? NSLog ( @"%@——1----" ,[ NSThread currentThread ]); ??? }];
??? [ blockOperation addExecutionBlock:^{
??????? NSLog ( @"%@—2-------" ,[ NSThread currentThread ]); ??? }];
以上有三個操作( opration1、 opration2、 blockOperation ) ?4個任務( download、 run、 %@——1——、 %@——2---- )
2、創建隊列
NSOperationQueue*queue = [[NSOperationQueuealloc]init];
// 最大并發數 ? 同一時間只能做 2 件事 ? 控制開線程的個數 ?queue.maxConcurrentOperationCount= 2;//2---3
// 任務在隊列中的優先級 ???/*
???? NSOperationQueuePriorityVeryLow = -8L,
???? NSOperationQueuePriorityLow = -4L,
???? NSOperationQueuePriorityNormal = 0,
???? NSOperationQueuePriorityHigh = 4,
???? NSOperationQueuePriorityVeryHigh = 8 ???? */ opration1.queuePriority= NSOperationQueuePriorityNormal;
操作依賴 // 操作依賴 ? 在添加到隊列之前 ? ---- 可以再不同隊列間的 operation 之間添加依賴 ? 不能相互依賴 //opration1依賴 opration2? 當opration2執行完畢后才能?執行opration1 ??? [opration1addDependency:opration2];
??? // 操作的監聽
??? // 當 blockOperation 中的操作完成后 在當前線程中執行下面操作
??? blockOperation. completionBlock= ^{
??? };
//添加到隊列中---------》異步執行???多個任務一定是并行操作[queueaddOperation:opration1]; [queueaddOperation:opration2]; [queueaddOperation:blockOperation];
自定義NSOperation
創建屬性 /**
?*? 下載圖片的隊列
?*/
@property(nonatomic,strong)NSOperationQueue *queue;
/** key:url value:operation對象*/ @property(nonatomic,strong)NSMutableDictionary *operations; /** key:url value:image對象*/ @property(nonatomic,strong)NSMutableDictionary *images;
懶加載 - (NSArray*)apps
{
??? if (!_apps) {
??????? NSArray *dictArray = [NSArrayarrayWithContentsOfFile:[[NSBundlemainBundle]pathForResource:@"apps.plist"ofType:nil]];
???????
??????? NSMutableArray *appArray = [NSMutableArrayarray];
??????? for (NSDictionary*dict in dictArray) {
??????????? HMApp *app = [HMAppappWithDict:dict];
??????????? [appArray addObject:app];
??????? }
??????? _apps = appArray;
??? }
??? return _apps;
}
- (NSOperationQueue*)queue
{
??? if (!_queue) {
??????? _queue = [[NSOperationQueuealloc]init];
??????? _queue.maxConcurrentOperationCount= 3;// 最大并發數 == 3
??? }
??? return _queue;
}
- (NSMutableDictionary*)operations
{
??? if (!_operations) {
??????? _operations = [NSMutableDictionarydictionary];
??? }
??? return _operations;
}
- (NSMutableDictionary*)images
{
??? if (!_images) {
??????? _images = [NSMutableDictionarydictionary];
??? }
??? return _images; }
cell中下載圖片的代碼 //顯示圖片
??? // 保證一個url對應一個HMDownloadOperation
??? // 保證一個url對應UIImage對象
???
??? UIImage *image = self.images[app.icon];
??? if (image) { //?內存緩存中有圖片
??????? cell.imageView.image= image;
??? } else { //?內存緩存中沒有圖片,得下載
//??????? cell.imageView.image = [UIImage imageNamed:@"57437179_42489b0"];
???????
??????? HMDownloadOperation *operation = self.operations[app.icon];
??????? if (operation) { // 正在下載
??????????? // ... 暫時不需要做其他事
???????????
??????? } else { // 沒有正在下載
??????????? // 創建操作
??????????? operation = [[HMDownloadOperationalloc]init];
??????????? operation.url= app.icon;
??????????? operation.delegate= self;
??????????? operation.indexPath= indexPath;
??????????? [self.queueaddOperation:operation];// 異步下載
???????????
??????????? self.operations[app.icon] = operation;
??????? } ??? }
#pragma mark - HMDownloadOperationDelegate
- ( void )downloadOperation:( HMDownloadOperation *)operation didFinishDownload:( UIImage *)image
{
??? // 1. 移除執行完畢的操作
??? [ self . operations removeObjectForKey :operation. url ];
???
??? if (image) {
??????? // 2. 將圖片放到緩存中 (images)
??????? self . images [operation. url ] = image;
???????
??????? // 3. 刷新表格
??????? [ self . tableView reloadRowsAtIndexPaths : @[ operation. indexPath ] withRowAnimation : UITableViewRowAnimationNone ];
???????
??????? // 3. 將圖片寫入沙盒
//??????? NSData *data = UIImagePNGRepresentation(image);
//??????? [data writeToFile:@"" atomically:<#(BOOL)#>];
??? }
??? }
tableView滾動的時候的操作 - (void)scrollViewWillBeginDragging:(UIScrollView*)scrollView
{
??? // 開始拖拽
??? // 暫停隊列
??? [self.queuesetSuspended:YES];
}
- (void)scrollViewWillEndDragging:(UIScrollView*)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inoutCGPoint *)targetContentOffset
{
??? [self.queuesetSuspended:NO]; }
自定義NSOperation的代碼 HMDownloadOperation.h @classHMDownloadOperation;
@protocolHMDownloadOperationDelegate <NSObject>
@optional
- (void)downloadOperation:(HMDownloadOperation*)operation didFinishDownload:(UIImage*)image;
@end
@interfaceHMDownloadOperation : NSOperation
@property(nonatomic,copy)NSString *url;
@property(nonatomic,strong)NSIndexPath *indexPath;
@property(nonatomic,weak)id<HMDownloadOperationDelegate> delegate; @end
HMDownloadOperation.m /**
?*? 在main方法中實現具體操作
?*/
- (void)main
{
??? @autoreleasepool {
??????? if (self.isCancelled)return;
???????
??????? NSURL *downloadUrl = [NSURLURLWithString:self.url];
??????? NSData *data = [NSDatadataWithContentsOfURL:downloadUrl];// 這行會比較耗時
???????
??????? if (self.isCancelled)return;
???????
??????? UIImage *image = [UIImageimageWithData:data];
???????
??????? if (self.isCancelled)return;
???????
??????? if ([self.delegaterespondsToSelector:@selector(downloadOperation:didFinishDownload:)]) {
??????????? dispatch_async(dispatch_get_main_queue(), ^{ // 回到主線程,傳遞圖片數據給代理對象
??????????????? [self.delegatedownloadOperation:selfdidFinishDownload:image];
??????????? });
??????? }
??? }
總結
- 上一篇: 如何优雅的用POI导入Excel文件
- 下一篇: fei