iOS学习之多线程
在iOS中提供了4中多線程的方式
以下4中方式:
pthread使用較少
? ? ?NSThread *current = [NSThread currentThread];
1、創(chuàng)建一個線程id
pthread_t threadId; pthrad_create(&threadId,NULL,run,NULL);
void *run(void *data) { ? ? ?return NULL; }
NSThread 一個NSThread對象就代表一條線程 使用更加面向?qū)ο?#xff0c;可以直接操作線程對象,oc 語言 ? 程序員進行管理 線程的生命周期
主線程相關(guān)用法 NSThread *current = [NSThread currentThread]; NSThread *mainThread = [NS] 判斷是不是主線程 [NSThread isMainThread];
線程的優(yōu)先級 NSThread?threadPriority -(BOOL)setThreadPriority:(double); 調(diào)度優(yōu)先級的取值范圍是0.0 — 1.0 值越大 優(yōu)先級越高
1、創(chuàng)建線程 *1 NSThread *thread = [[ NSThread alloc ] initWithTarget : self? selector : @selector (run:) object : @"aaa" ]; // 開啟線程 [thread start];
*2 創(chuàng)建后就執(zhí)行 ?自動啟動 無法對線程進行更詳細的設(shè)置 [NSThread detachNewThreadS elector : @selector (run:) object : @"aaa" ]; ]
3*// 隱式創(chuàng)建 [self performSelectorInBackground:@selector(run:)withObject:@"aa"];
-(void)run { ?????
}
線程的生命周期 新建——————》線程對象進入可調(diào)度線程池(就緒狀態(tài))——————》運行狀態(tài)(如果cpu調(diào)度當前線程)——————》調(diào)用sleep方法?當前線程從調(diào)度池 ,但是該線程仍然在內(nèi)存中?—————》當阻塞結(jié)束 當前進程進入就緒狀態(tài)
線程任務(wù)執(zhí)行完畢、異常退出、強制退出 ?都是 線程進入死亡狀態(tài)(仍然在內(nèi)存中) //阻塞線程 +(void)sleepUntiData:(NSDate:)date; + ? (void)sleepForTimeInterval:(NSTimeInterval)ti; 強制停止線程 +(void)exit; ? 線程進入死亡狀態(tài)
線程加鎖(線程同步)—》多條線程按順序的執(zhí)行任務(wù) ?互斥鎖 就是使用了線程同步技術(shù) NSLock *lock = [[NSLock alloc]init]; 線程讀之前加鎖(只能用一把鎖)
? @synchronized(self){ 加鎖 ? ? ?大括號內(nèi)的代碼有線程安全問題代碼要進行加鎖操作
}大括號結(jié)束解鎖
線程間通信 兩個進行線程間通信的方法 下面方法在主線程中執(zhí)行 settingImage 方法 [self performSelectorOnMainThread:@selector(settingImage:) withObject:image waitUntilDone:NO];
下面方法是在 onThread指定的線程中執(zhí)行某個方法 [self performSelector:<#(SEL)#> onThread:<#(NSThread *)#> withObject:<#(id)#> waitUntilDone:<#(BOOL)#> modes:<#(NSArray *)#>]
對ui界面的操作要在主線程中執(zhí)行,例如以下載圖片為例可以使用 下面方法的意思是:在主線程中調(diào)用imageView 的 setImage:方法將imageView的image設(shè)置為 下載好的image 其中 waitUntilDone設(shè)置為NO為 調(diào)用完這個方法會立即返回? ? ?waitUntilDone設(shè)置為YES 這個方法會在主線程執(zhí)行完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? 牛逼的中樞調(diào)度器
優(yōu)勢: 1、多核的并行運算提出的解決方案 2、會自動利用更多的cpu內(nèi)核 3、自動關(guān)了線程的生命周期(創(chuàng)建線程、調(diào)度任務(wù)、銷毀線程) 4、不需要編寫任何線程管理代碼 ? 只需要告訴GCD 要執(zhí)行的任務(wù) ?GCD 會自動創(chuàng)建隊列執(zhí)行任務(wù)
使用:兩個步驟 ? 定制任務(wù) ? 添加定制任務(wù)到隊列中 —》 GCD會自動將隊列中的任務(wù)取出,放到對應(yīng)的線程中執(zhí)行 任務(wù)的取出遵循 FIFO ?先進先出
//凡是函數(shù)名種帶有create\copy\new\retain等字眼,都需要在不需要使用這個數(shù)據(jù)的時候進行release
// GCD的數(shù)據(jù)類型在ARC環(huán)境下不需要再做release // CF(Core Foundation) 的數(shù)據(jù)類型在 ARC 環(huán)境下還是需要再做 release
/**
?*? 全局并發(fā)隊列
?*/
#define global_queue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 )
/**
?*? 主隊列
?*/ #define main_queue dispatch_get_main_queue()
基本使用:(同步函數(shù)和異步函數(shù)———決定了要不要開啟新的線程) 1、2個用來執(zhí)行任務(wù)的函數(shù) 同步:在當前線程中執(zhí)行任務(wù) 不具備開啟新線程的能力 dispatch_sync(<#dispatch_queue_t queue#>, <#^(void)block#>)
異步:在另一條線程中執(zhí)行 ?具備了開啟新線程的能力 dispatch_async(<#dispatch_queue_t queue#>, <#^(void)block#>)
特殊情況:異步函數(shù)的任務(wù)添加在主隊列中(往主隊列中添加一個同步任務(wù)), 任務(wù)在主線程執(zhí)行,不會開啟新的線程
2、隊列 分為兩大類(并發(fā)和串行————決定了任務(wù)的執(zhí)行方式) 并發(fā)隊列:可以讓多個任務(wù)并發(fā)同時執(zhí)行(自動開啟多條線程) ? 在異步函數(shù)中執(zhí)行
串行隊列:讓任務(wù)一個接著一個執(zhí)行
并發(fā)隊列:GCD默認已經(jīng)提供了全局的并發(fā)隊列供整應(yīng)用程序使用,不需要手動創(chuàng)建
獲取一個全局的默認優(yōu)先級的并發(fā)隊列 dispatch_queue_tqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
添加任務(wù)到隊列中執(zhí)行任務(wù)
1、 用 dispatch_async 異步函數(shù)往并發(fā)隊列中添加任務(wù)—————》同時開啟了3個子線程 - (void)asyncGlobalQueue
{ ???//?獲取一個全局的默認優(yōu)先級的并發(fā)隊列 ???dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
???
??? // 2.添加任務(wù)到隊列中執(zhí)行
??? dispatch_async(queue, ^{
??????? NSLog(@"----下載圖片1-----%@", [NSThreadcurrentThread]);
??? });
??? dispatch_async(queue, ^{
??????? NSLog(@"----下載圖片2-----%@", [NSThreadcurrentThread]);
??? });
??? dispatch_async(queue, ^{
??????? NSLog(@"----下載圖片3-----%@", [NSThreadcurrentThread]); ??? }); ???// 總結(jié):同時開啟了3個線程 }
2、用dispatch_async異步函數(shù) 往串行隊列中添加任務(wù)————————》只開了一個線程執(zhí)行任務(wù) - (void)asyncSerialQueue {
??? // 1.創(chuàng)建串行隊列
??? dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue",NULL);
???
??? // 2.添加任務(wù)到隊列中執(zhí)行
??? dispatch_async(queue, ^{
??????? NSLog(@"----下載圖片1-----%@", [NSThreadcurrentThread]);
??? });
??? dispatch_async(queue, ^{
??????? NSLog(@"----下載圖片2-----%@", [NSThreadcurrentThread]);
??? });
??? dispatch_async(queue, ^{
??????? NSLog(@"----下載圖片3-----%@", [NSThreadcurrentThread]);
??? }); ? ?//總結(jié):只開1個線程執(zhí)行任務(wù) }
3、用dispatch_sync同步函數(shù)往并發(fā)隊列中添加任務(wù)—————》不會開啟新的線程,并發(fā)隊列失去了并發(fā)的功能 - (void)syncGlobalQueue
{
??? // 1.獲得全局的并發(fā)隊列
??? dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
???
??? // 2.添加任務(wù)到隊列中執(zhí)行
??? dispatch_sync(queue, ^{
??????? NSLog(@"----下載圖片1-----%@", [NSThreadcurrentThread]);
??? });
??? dispatch_sync(queue, ^{
??????? NSLog(@"----下載圖片2-----%@", [NSThreadcurrentThread]);
??? });
??? dispatch_sync(queue, ^{
??????? NSLog(@"----下載圖片3-----%@", [NSThreadcurrentThread]);
??? });
???
??? // 總結(jié):不會開啟新的線程,并發(fā)隊列失去了并發(fā)的功能 }
4、 用 dispatch_sync 同步函數(shù)往串行列中添加任務(wù)—————》不會開啟新的線程 - (void)syncSerialQueue
{ ???// 1.創(chuàng)建串行隊列(串行隊列) ???dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue",NULL);
???
??? // 2.添加任務(wù)到隊列中執(zhí)行
??? dispatch_sync(queue, ^{
??????? NSLog(@"----下載圖片1-----%@", [NSThreadcurrentThread]);
??? });
??? dispatch_sync(queue, ^{
??????? NSLog(@"----下載圖片2-----%@", [NSThreadcurrentThread]);
??? });
??? dispatch_sync(queue, ^{
??????? NSLog(@"----下載圖片3-----%@", [NSThreadcurrentThread]);
??? });
???
??? // 3.釋放資源
//??? dispatch_release(queue); ? // MRC(非ARC)
???
??? // 總結(jié):不會開啟新的線程 }
5、 用 dispatch_sync 同步函數(shù) , 在主線程中往主隊列中添加任務(wù) : 任務(wù)無法往下執(zhí)行———————>產(chǎn)生死鎖(死循環(huán)) - (void)syncMainQueue
{
??? // 1.獲得主隊列
??? dispatch_queue_t queue = dispatch_get_main_queue();
??? ???// 2.添加任務(wù)到隊列中執(zhí)行 ?????(queue, ^{ ???????NSLog(@"----下載圖片1-----%@", [NSThreadcurrentThread]);
??? });
//??? dispatch_sync(queue, ^{
//??????? NSLog(@"----下載圖片2-----%@", [NSThread currentThread]);
//??? });
//??? dispatch_sync(queue, ^{
//??????? NSLog(@"----下載圖片3-----%@", [NSThread currentThread]);
//??? });
}
6、 使用dispatch_async異步函數(shù),在主線程中往主隊列中添加任務(wù) 特殊情況:異步函數(shù)的任務(wù)添加在主隊列中(往主隊列中添加一個同步任務(wù)), 任務(wù)在主線程執(zhí)行,不會開啟新的線程
- ( void )asyncMainQueue { ??? // 1.獲得主隊列(串行隊列) ??? dispatch_queue_t queue = dispatch_get_main_queue ();
???
??? // 2. 添加任務(wù)到隊列中 執(zhí)行
??? dispatch_async (queue, ^{
??????? NSLog ( @"---- 下載圖片 1-----%@" , [ NSThread currentThread ]);
??? }); }
同步函數(shù)(sync) 并發(fā)隊列:不會開啟線程 串行隊列:不會開啟線程 異步函數(shù)(async) 并發(fā)隊列:開啟多個線程 串行隊列:開啟一條線程
使用GCD進行異步圖片下載 1、獲取全局并發(fā)隊列 ??? dispatch_queue_tqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0); 2、調(diào)用異步(同步)函數(shù)下載圖片 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、延遲執(zhí)行? *1 ?[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
*2 利用GCD 進行延遲執(zhí)行(推薦) dispatch_queue_t queue =? dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //??? /**
// ??? *? 2 秒鐘后 ? 在隊列 queue 中執(zhí)行 block 中的任務(wù)
// ??? */
//??? dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), queue, ^{
//??? //??? });
2、一次性代碼 ? 使某一段代碼在整個程序運行過程中只執(zhí)行一次,用于創(chuàng)建單? dispatch_once static id _instance; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ ??????_instance = [[self alloc]init]; });
3、隊列組 ?當程序需要執(zhí)行完多個耗時操作后 在執(zhí)行某個其他操作的時候使用 如:下載兩張圖片 ?下載完成后進行拼接顯示在imageView上 *1、?創(chuàng)建一個組 dispatch_group_t group = dispatch_group_create(); *2、開啟一個任務(wù)下載圖片1 __block? UIImage *image1 = nil;
??? //開啟一個任務(wù)下載圖片1
??? dispatch_group_async(group, global_queue, ^{
??????? image1 = [self imageWithUrl:@"1"]; ??? }); *3?開啟一個任務(wù)下載圖片2 ?? __block UIImage *image2 = nil;
??? //開啟一個任務(wù)下載圖片2 ??? dispatch_group_async(group, global_queue, ^{ ? ? image2 = [self imageWithUrl:@"2"]; ??? }); 4* 等待group中的所有任務(wù)都執(zhí)行完畢后 ?在執(zhí)行其他操作 ??? 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 的子類 創(chuàng)建任務(wù) ? NSInvocationOperation 、?NSBlockOperation
1、封裝任務(wù) ???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個任務(wù)( download、 run、 %@——1——、 %@——2---- )
2、創(chuàng)建隊列
NSOperationQueue*queue = [[NSOperationQueuealloc]init];
// 最大并發(fā)數(shù) ? 同一時間只能做 2 件事 ? 控制開線程的個數(shù) ?queue.maxConcurrentOperationCount= 2;//2---3
// 任務(wù)在隊列中的優(yōu)先級 ???/*
???? NSOperationQueuePriorityVeryLow = -8L,
???? NSOperationQueuePriorityLow = -4L,
???? NSOperationQueuePriorityNormal = 0,
???? NSOperationQueuePriorityHigh = 4,
???? NSOperationQueuePriorityVeryHigh = 8 ???? */ opration1.queuePriority= NSOperationQueuePriorityNormal;
操作依賴 // 操作依賴 ? 在添加到隊列之前 ? ---- 可以再不同隊列間的 operation 之間添加依賴 ? 不能相互依賴 //opration1依賴 opration2? 當opration2執(zhí)行完畢后才能?執(zhí)行opration1 ??? [opration1addDependency:opration2];
??? // 操作的監(jiān)聽
??? // 當 blockOperation 中的操作完成后 在當前線程中執(zhí)行下面操作
??? blockOperation. completionBlock= ^{
??? };
//添加到隊列中---------》異步執(zhí)行???多個任務(wù)一定是并行操作[queueaddOperation:opration1]; [queueaddOperation:opration2]; [queueaddOperation:blockOperation];
自定義NSOperation
創(chuàng)建屬性 /**
?*? 下載圖片的隊列
?*/
@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;// 最大并發(fā)數(shù) == 3
??? }
??? return _queue;
}
- (NSMutableDictionary*)operations
{
??? if (!_operations) {
??????? _operations = [NSMutableDictionarydictionary];
??? }
??? return _operations;
}
- (NSMutableDictionary*)images
{
??? if (!_images) {
??????? _images = [NSMutableDictionarydictionary];
??? }
??? return _images; }
cell中下載圖片的代碼 //顯示圖片
??? // 保證一個url對應(yīng)一個HMDownloadOperation
??? // 保證一個url對應(yīng)UIImage對象
???
??? UIImage *image = self.images[app.icon];
??? if (image) { //?內(nèi)存緩存中有圖片
??????? cell.imageView.image= image;
??? } else { //?內(nèi)存緩存中沒有圖片,得下載
//??????? cell.imageView.image = [UIImage imageNamed:@"57437179_42489b0"];
???????
??????? HMDownloadOperation *operation = self.operations[app.icon];
??????? if (operation) { // 正在下載
??????????? // ... 暫時不需要做其他事
???????????
??????? } else { // 沒有正在下載
??????????? // 創(chuàng)建操作
??????????? 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. 移除執(zhí)行完畢的操作
??? [ 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方法中實現(xiàn)具體操作
?*/
- (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(), ^{ // 回到主線程,傳遞圖片數(shù)據(jù)給代理對象
??????????????? [self.delegatedownloadOperation:selfdidFinishDownload:image];
??????????? });
??????? }
??? }
總結(jié)
- 上一篇: 如何优雅的用POI导入Excel文件
- 下一篇: fei