概覽
大家都知道赵颅,在開發(fā)過(guò)程中應(yīng)該盡可能減少用戶等待時(shí)間蛋欣,讓程序盡可能快的完成運(yùn)算病曾∈г荩可是無(wú)論是哪種語(yǔ)言開發(fā)的程序最終往往轉(zhuǎn)換成匯編語(yǔ)言進(jìn)而解釋成機(jī)器碼來(lái)執(zhí)行斤斧。但是機(jī)器碼是按順序執(zhí)行的菩掏,一個(gè)復(fù)雜的多步操作只能一步步按順序逐個(gè)執(zhí)行。改變這種狀況可以從兩個(gè)角度出發(fā):對(duì)于單核處理器遂填,可以將多個(gè)步驟放到不同的線程铲觉,這樣一來(lái)用戶完成UI操作后其他后續(xù)任務(wù)在其他線程中,當(dāng)CPU空閑時(shí)會(huì)繼續(xù)執(zhí)行吓坚,而此時(shí)對(duì)于用戶而言可以繼續(xù)進(jìn)行其他操作撵幽;對(duì)于多核處理器,如果用戶在UI線程中完成某個(gè)操作之后礁击,其他后續(xù)操作在別的線程中繼續(xù)執(zhí)行盐杂,用戶同樣可以繼續(xù)進(jìn)行其他UI操作,與此同時(shí)前一個(gè)操作的后續(xù)任務(wù)可以分散到多個(gè)空閑CPU中繼續(xù)執(zhí)行(當(dāng)然具體調(diào)度順序要根據(jù)程序設(shè)計(jì)而定)客税,及解決了線程阻塞又提高了運(yùn)行效率况褪。蘋果從iPad2 開始使用雙核A5處理器(iPhone中從iPhone 4S開始使用),A7中還加入了協(xié)處理器更耻,如何充分發(fā)揮這些處理器的性能確實(shí)值得思考测垛。今天將重點(diǎn)分析iOS多線程開發(fā):
簡(jiǎn)介
iOS多線程
解決線程阻塞問(wèn)題
多線程并發(fā)
線程狀態(tài)
擴(kuò)展-NSObject分類擴(kuò)展
NSInvocationOperation
NSBlockOperation
線程執(zhí)行順序
串行隊(duì)列
并發(fā)隊(duì)列
其他任務(wù)執(zhí)行方法
NSLock同步鎖
@synchronized代碼塊
擴(kuò)展--使用GCD解決資源搶占問(wèn)題
擴(kuò)展--控制線程通信
目錄
當(dāng)用戶播放音頻、下載資源秧均、進(jìn)行圖像處理時(shí)往往希望做這些事情的時(shí)候其他操作不會(huì)被中斷或者希望這些操作過(guò)程中更加順暢食侮。在單線程中一個(gè)線程只能做一件事情,一件事情處理不完另一件事就不能開始目胡,這樣勢(shì)必影響用戶體驗(yàn)锯七。早在單核處理器時(shí)期就有多線程,這個(gè)時(shí)候多線程更多的用于解決線程阻塞造成的用戶等待(通常是操作完UI后用戶不再干涉誉己,其他線程在等待隊(duì)列中眉尸,CPU一旦空閑就繼續(xù)執(zhí)行,不影響用戶其他UI操作)巨双,其處理能力并沒(méi)有明顯的變化噪猾。如今無(wú)論是移動(dòng)操作系統(tǒng)還是PC、服務(wù)器都是多核處理器筑累,于是“并行運(yùn)算”就更多的被提及袱蜡。一件事情我們可以分成多個(gè)步驟,在沒(méi)有順序要求的情況下使用多線程既能解決線程阻塞又能充分利用多核處理器運(yùn)行能力慢宗。
下圖反映了一個(gè)包含8個(gè)操作的任務(wù)在一個(gè)有兩核心的CPU中創(chuàng)建四個(gè)線程運(yùn)行的情況坪蚁。假設(shè)每個(gè)核心有兩個(gè)線程,那么每個(gè)CPU中兩個(gè)線程會(huì)交替執(zhí)行镜沽,兩個(gè)CPU之間的操作會(huì)并行運(yùn)算敏晤。單就一個(gè)CPU而言兩個(gè)線程可以解決線程阻塞造成的不流暢問(wèn)題,其本身運(yùn)行效率并沒(méi)有提高缅茉,多CPU的并行運(yùn)算才真正解決了運(yùn)行效率問(wèn)題茵典,這也正是并發(fā)和并行的區(qū)別。當(dāng)然宾舅,不管是多核還是單核開發(fā)人員不用過(guò)多的擔(dān)心统阿,因?yàn)槿蝿?wù)具體分配給幾個(gè)CPU運(yùn)算是由系統(tǒng)調(diào)度的彩倚,開發(fā)人員不用過(guò)多關(guān)心系統(tǒng)有幾個(gè)CPU。開發(fā)人員需要關(guān)心的是線程之間的依賴關(guān)系扶平,因?yàn)橛行┎僮鞅仨氃谀硞€(gè)操作完成完才能執(zhí)行帆离,如果不能保證這個(gè)順序勢(shì)必會(huì)造成程序問(wèn)題。
在iOS中每個(gè)進(jìn)程啟動(dòng)后都會(huì)建立一個(gè)主線程(UI線程)结澄,這個(gè)線程是其他線程的父線程哥谷。由于在iOS中除了主線程,其他子線程是獨(dú)立于Cocoa Touch的麻献,所以只有主線程可以更新UI界面(新版iOS中们妥,使用其他線程更新UI可能也能成功,但是不推薦)勉吻。iOS中多線程使用并不復(fù)雜监婶,關(guān)鍵是如何控制好各個(gè)線程的執(zhí)行順序、處理好資源競(jìng)爭(zhēng)問(wèn)題齿桃。常用的多線程開發(fā)有三種方式:
1.NSThread
2.NSOperation
3.GCD
三種方式是隨著iOS的發(fā)展逐漸引入的惑惶,所以相比而言后者比前者更加簡(jiǎn)單易用,并且GCD也是目前蘋果官方比較推薦的方式(它充分利用了多核處理器的運(yùn)算性能)短纵。做過(guò).Net開發(fā)的朋友不難發(fā)現(xiàn)其實(shí)這三種開發(fā)方式 剛好對(duì)應(yīng).Net中的多線程带污、線程池和異步調(diào)用,因此在文章中也會(huì)對(duì)比講解香到。
NSThread是輕量級(jí)的多線程開發(fā)鱼冀,使用起來(lái)也并不復(fù)雜,但是使用NSThread需要自己管理線程生命周期悠就∏鳎可以使用對(duì)象方法+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument直接將操作添加到線程中并啟動(dòng),也可以使用對(duì)象方法- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(id)argument創(chuàng)建一個(gè)線程對(duì)象理卑,然后調(diào)用start方法啟動(dòng)線程翘紊。
在資源下載過(guò)程中蔽氨,由于網(wǎng)絡(luò)原因有時(shí)候很難保證下載時(shí)間藐唠,如果不使用多線程可能用戶完成一個(gè)下載操作需要長(zhǎng)時(shí)間的等待,這個(gè)過(guò)程中無(wú)法進(jìn)行其他操作鹉究。下面演示一個(gè)采用多線程下載圖片的過(guò)程宇立,在這個(gè)示例中點(diǎn)擊按鈕會(huì)啟動(dòng)一個(gè)線程去下載圖片,下載完成后使用UIImageView將圖片顯示到界面中自赔÷栲冢可以看到用戶點(diǎn)擊完下載按鈕后,不管圖片是否下載完成都可以繼續(xù)操作界面绍妨,不會(huì)造成阻塞润脸。
//
// ?NSThread實(shí)現(xiàn)多線程
// ?MultiThread
//
// ?Created by Kenshin Cui on 14-3-22.
// ?Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//#import"KCMainViewController.h"@interfaceKCMainViewController (){
UIImageView *_imageView;
}
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self layoutUI];
}#pragmamark 界面布局
-(void)layoutUI{
_imageView =[[UIImageView alloc]initWithFrame:[UIScreen mainScreen].applicationFrame];
_imageView.contentMode=UIViewContentModeScaleAspectFit;
[self.view addSubview:_imageView];
UIButton *button=[UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame=CGRectMake(50, 500, 220, 25);
[button setTitle:@"加載圖片"forState:UIControlStateNormal];//添加方法[button addTarget:self action:@selector(loadImageWithMultiThread) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
}#pragmamark 將圖片顯示到界面
-(void)updateImage:(NSData *)imageData{
UIImage *image=[UIImage imageWithData:imageData];
_imageView.image=image;
}#pragmamark 請(qǐng)求圖片數(shù)據(jù)
-(NSData *)requestData{//對(duì)于多線程操作建議把線程操作放到@autoreleasepool中@autoreleasepool {
NSURL *url=[NSURL URLWithString:@"http://images.apple.com/iphone-6/overview/images/biggest_right_large.png"];
NSData *data=[NSData dataWithContentsOfURL:url];returndata;
}
}#pragmamark 加載圖片
-(void)loadImage{//請(qǐng)求數(shù)據(jù)NSData *data= [self requestData];/*將數(shù)據(jù)顯示到UI控件,注意只能在主線程中更新UI,
另外performSelectorOnMainThread方法是NSObject的分類方法柬脸,每個(gè)NSObject對(duì)象都有此方法,
它調(diào)用的selector方法是當(dāng)前調(diào)用控件的方法毙驯,例如使用UIImageView調(diào)用的時(shí)候selector就是UIImageView的方法
Object:代表調(diào)用方法的參數(shù),不過(guò)只能傳遞一個(gè)參數(shù)(如果有多個(gè)參數(shù)請(qǐng)使用對(duì)象進(jìn)行封裝)
waitUntilDone:是否線程任務(wù)完成執(zhí)行
*/[self performSelectorOnMainThread:@selector(updateImage:) withObject:data waitUntilDone:YES];
}#pragmamark 多線程下載圖片
-(void)loadImageWithMultiThread{//方法1:使用對(duì)象方法
//創(chuàng)建一個(gè)線程倒堕,第一個(gè)參數(shù)是請(qǐng)求的操作,第二個(gè)參數(shù)是操作方法的參數(shù)
// ? ?NSThread *thread=[[NSThread alloc]initWithTarget:self selector:@selector(loadImage) object:nil];
// ? ?//啟動(dòng)一個(gè)線程爆价,注意啟動(dòng)一個(gè)線程并非就一定立即執(zhí)行垦巴,而是處于就緒狀態(tài),當(dāng)系統(tǒng)調(diào)度時(shí)才真正執(zhí)行
// ? ?[thread start];
//方法2:使用類方法[NSThread detachNewThreadSelector:@selector(loadImage) toTarget:self withObject:nil];
}
@end
運(yùn)行效果:
程序比較簡(jiǎn)單铭段,但是需要注意執(zhí)行步驟:當(dāng)點(diǎn)擊了“加載圖片”按鈕后啟動(dòng)一個(gè)新的線程骤宣,這個(gè)線程在演示中大概用了5s左右,在這5s內(nèi)UI線程是不會(huì)阻塞的序愚,用戶可以進(jìn)行其他操作憔披,大約5s之后圖片下載完成,此時(shí)調(diào)用UI線程將圖片顯示到界面中(這個(gè)過(guò)程瞬間完成)展运。另外前面也提到過(guò)活逆,更新UI的時(shí)候使用UI線程,這里調(diào)用了NSObject的分類擴(kuò)展方法拗胜,調(diào)用UI線程完成更新蔗候。
上面這個(gè)演示并沒(méi)有演示多個(gè)子線程操作之間的關(guān)系,現(xiàn)在不妨在界面中多加載幾張圖片埂软,每個(gè)圖片都來(lái)自遠(yuǎn)程請(qǐng)求锈遥。
大家應(yīng)該注意到不管是使用+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument、- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(id)argument方法還是使用- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait方法都只能傳一個(gè)參數(shù)勘畔,由于更新圖片需要傳遞UIImageView的索引和圖片數(shù)據(jù)所灸,因此這里不妨定義一個(gè)類保存圖片索引和圖片數(shù)據(jù)以供后面使用。
KCImageData.h
//
// ?KCImageData.h
// ?MultiThread
//
// ?Created by Kenshin Cui on 14-3-22.
// ?Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//#import@interfaceKCImageData : NSObject#pragmamark 索引
@property(nonatomic,assign)intindex;#pragmamark 圖片數(shù)據(jù)
@property(nonatomic,strong) NSData *data;
@end
接下來(lái)將創(chuàng)建多個(gè)UIImageView并創(chuàng)建多個(gè)線程用于往UIImageView中填充圖片炫七。
KCMainViewController.m
//
// ?NSThread實(shí)現(xiàn)多線程
// ?MultiThread
//
// ?Created by Kenshin Cui on 14-3-22.
// ?Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//#import"KCMainViewController.h"#import"KCImageData.h"#defineROW_COUNT 5#defineCOLUMN_COUNT 3#defineROW_HEIGHT 100#defineROW_WIDTH ROW_HEIGHT#defineCELL_SPACING 10
@interfaceKCMainViewController (){
NSMutableArray *_imageViews;
}
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self layoutUI];
}#pragmamark 界面布局
-(void)layoutUI{//創(chuàng)建多個(gè)圖片控件用于顯示圖片_imageViews=[NSMutableArrayarray];for(intr=0; r
UIImageView *imageView=[[UIImageView alloc]initWithFrame:CGRectMake(c*ROW_WIDTH+(c*CELL_SPACING), r*ROW_HEIGHT+(r*CELL_SPACING ? ? ? ? ? ? ? ? ? ? ? ? ? ), ROW_WIDTH, ROW_HEIGHT)];
imageView.contentMode=UIViewContentModeScaleAspectFit;// ? ? ? ? ? ?imageView.backgroundColor=[UIColor redColor];[self.view addSubview:imageView];
[_imageViews addObject:imageView];
}
}
UIButton *button=[UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame=CGRectMake(50, 500, 220, 25);
[button setTitle:@"加載圖片"forState:UIControlStateNormal];//添加方法[button addTarget:self action:@selector(loadImageWithMultiThread) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
}#pragmamark 將圖片顯示到界面
-(void)updateImage:(KCImageData *)imageData{
UIImage *image=[UIImage imageWithData:imageData.data];
UIImageView *imageView= _imageViews[imageData.index];
imageView.image=image;
}#pragmamark 請(qǐng)求圖片數(shù)據(jù)
-(NSData *)requestData:(int)index{//對(duì)于多線程操作建議把線程操作放到@autoreleasepool中@autoreleasepool {
NSURL *url=[NSURL URLWithString:@"http://images.apple.com/iphone-6/overview/images/biggest_right_large.png"];
NSData *data=[NSData dataWithContentsOfURL:url];returndata;
}
}#pragmamark 加載圖片
-(void)loadImage:(NSNumber *)index{// ? ?NSLog(@"%i",i);
//currentThread方法可以取得當(dāng)前操作線程N(yùn)SLog(@"current thread:%@",[NSThread currentThread]);inti=[index integerValue];// ? ?NSLog(@"%i",i);//未必按順序輸出NSData *data= [self requestData:i];
KCImageData *imageData=[[KCImageData alloc]init];
imageData.index=i;
imageData.data=data;
[self performSelectorOnMainThread:@selector(updateImage:) withObject:imageData waitUntilDone:YES];
}#pragmamark 多線程下載圖片
-(void)loadImageWithMultiThread{//創(chuàng)建多個(gè)線程用于填充圖片for(inti=0; i
thread.name=[NSString stringWithFormat:@"myThread%i",i];//設(shè)置線程名稱[thread start];
}
}
@end
通過(guò)NSThread的currentThread可以取得當(dāng)前操作的線程爬立,其中會(huì)記錄線程名稱name和編號(hào)number,需要注意主線程編號(hào)永遠(yuǎn)為1万哪。多個(gè)線程雖然按順序啟動(dòng)侠驯,但是實(shí)際執(zhí)行未必按照順序加載照片(loadImage:方法未必依次創(chuàng)建,可以通過(guò)在loadImage:中打印索引查看)奕巍,因?yàn)榫€程啟動(dòng)后僅僅處于就緒狀態(tài)吟策,實(shí)際是否執(zhí)行要由CPU根據(jù)當(dāng)前狀態(tài)調(diào)度。
從上面的運(yùn)行效果大家不難發(fā)現(xiàn)的止,圖片并未按順序加載檩坚,原因有兩個(gè):第一,每個(gè)線程的實(shí)際執(zhí)行順序并不一定按順序執(zhí)行(雖然是按順序啟動(dòng));第二匾委,每個(gè)線程執(zhí)行時(shí)實(shí)際網(wǎng)絡(luò)狀況很可能不一致拖叙。當(dāng)然網(wǎng)絡(luò)問(wèn)題無(wú)法改變,只能盡可能讓網(wǎng)速更快赂乐,但是可以改變線程的優(yōu)先級(jí)憋沿,讓15個(gè)線程優(yōu)先執(zhí)行某個(gè)線程。線程優(yōu)先級(jí)范圍為0~1沪猴,值越大優(yōu)先級(jí)越高辐啄,每個(gè)線程的優(yōu)先級(jí)默認(rèn)為0.5。修改圖片下載方法如下运嗜,改變最后一張圖片加載的優(yōu)先級(jí)壶辜,這樣可以提高它被優(yōu)先加載的幾率,但是它也未必就第一個(gè)加載担租。因?yàn)槭紫绕渌€程是先啟動(dòng)的砸民,其次網(wǎng)絡(luò)狀況我們沒(méi)辦法修改:
-(void)loadImageWithMultiThread{
NSMutableArray *threads=[NSMutableArrayarray];intcount=ROW_COUNT*COLUMN_COUNT;//創(chuàng)建多個(gè)線程用于填充圖片for(inti=0; i
thread.name=[NSString stringWithFormat:@"myThread%i",i];//設(shè)置線程名稱if(i==(count-1)){
thread.threadPriority=1.0;
}else{
thread.threadPriority=0.0;
}
[threads addObject:thread];
}for(inti=0; i
NSThread *thread=threads[i];
[thread start];
}
}
在線程操作過(guò)程中可以讓某個(gè)線程休眠等待,優(yōu)先執(zhí)行其他線程操作奋救,而且在這個(gè)過(guò)程中還可以修改某個(gè)線程的狀態(tài)或者終止某個(gè)指定線程岭参。為了解決上面優(yōu)先加載最后一張圖片的問(wèn)題,不妨讓其他線程先休眠一會(huì)等待最后一個(gè)線程執(zhí)行尝艘。修改圖片加載方法如下即可:
-(NSData *)requestData:(int)index{//對(duì)于多線程操作建議把線程操作放到@autoreleasepool中@autoreleasepool {//對(duì)非最后一張圖片加載線程休眠2秒if(index!=(ROW_COUNT*COLUMN_COUNT-1)) {
[NSThread sleepForTimeInterval:2.0];
}
NSURL *url=[NSURL URLWithString:_imageNames[index]];
NSData *data=[NSData dataWithContentsOfURL:url];returndata;
}
}
在這里讓其他線程休眠2秒演侯,此時(shí)你就會(huì)看到最后一張圖片總是第一個(gè)加載(除非網(wǎng)速特別差)。
線程狀態(tài)分為isExecuting(正在執(zhí)行)背亥、isFinished(已經(jīng)完成)秒际、isCancellled(已經(jīng)取消)三種。其中取消狀態(tài)程序可以干預(yù)設(shè)置狡汉,只要調(diào)用線程的cancel方法即可娄徊。但是需要注意在主線程中僅僅能設(shè)置線程狀態(tài),并不能真正停止當(dāng)前線程盾戴,如果要終止線程必須在線程中調(diào)用exist方法寄锐,這是一個(gè)靜態(tài)方法,調(diào)用該方法可以退出當(dāng)前線程尖啡。
假設(shè)在圖片加載過(guò)程中點(diǎn)擊停止按鈕讓沒(méi)有完成的線程停止加載橄仆,可以改造程序如下:
//
// ?NSThread實(shí)現(xiàn)多線程
// ?MultiThread
//
// ?Created by Kenshin Cui on 14-3-22.
// ?Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//#import"KCMainViewController.h"#import"KCImageData.h"#defineROW_COUNT 5#defineCOLUMN_COUNT 3#defineROW_HEIGHT 100#defineROW_WIDTH ROW_HEIGHT#defineCELL_SPACING 10
@interfaceKCMainViewController (){
NSMutableArray *_imageViews;
NSMutableArray *_imageNames;
NSMutableArray *_threads;
}
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self layoutUI];
}#pragmamark 界面布局
-(void)layoutUI{//創(chuàng)建多個(gè)圖片空間用于顯示圖片_imageViews=[NSMutableArrayarray];for(intr=0; r
UIImageView *imageView=[[UIImageView alloc]initWithFrame:CGRectMake(c*ROW_WIDTH+(c*CELL_SPACING), r*ROW_HEIGHT+(r*CELL_SPACING ? ? ? ? ? ? ? ? ? ? ? ? ? ), ROW_WIDTH, ROW_HEIGHT)];
imageView.contentMode=UIViewContentModeScaleAspectFit;// ? ? ? ? ? ?imageView.backgroundColor=[UIColor redColor];[self.view addSubview:imageView];
[_imageViews addObject:imageView];
}
}//加載按鈕UIButton *buttonStart=[UIButton buttonWithType:UIButtonTypeRoundedRect];
buttonStart.frame=CGRectMake(50, 500, 100, 25);
[buttonStart setTitle:@"加載圖片"forState:UIControlStateNormal];
[buttonStart addTarget:self action:@selector(loadImageWithMultiThread) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:buttonStart];//停止按鈕UIButton *buttonStop=[UIButton buttonWithType:UIButtonTypeRoundedRect];
buttonStop.frame=CGRectMake(160, 500, 100, 25);
[buttonStop setTitle:@"停止加載"forState:UIControlStateNormal];
[buttonStop addTarget:self action:@selector(stopLoadImage) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:buttonStop];//創(chuàng)建圖片鏈接_imageNames=[NSMutableArrayarray];
[_imageNames addObject:@for(inti=0; i
[_imageNames addObject:[NSString stringWithFormat:@"http://images.cnblogs.com/cnblogs_com/kenshincui/613474/o_%i.jpg",i]];
}}#pragmamark 將圖片顯示到界面
-(void)updateImage:(KCImageData *)imageData{
UIImage *image=[UIImage imageWithData:imageData.data];
UIImageView *imageView= _imageViews[imageData.index];
imageView.image=image;
}#pragmamark 請(qǐng)求圖片數(shù)據(jù)
-(NSData *)requestData:(int)index{//對(duì)于多線程操作建議把線程操作放到@autoreleasepool中@autoreleasepool {
NSURL *url=[NSURL URLWithString:_imageNames[index]];
NSData *data=[NSData dataWithContentsOfURL:url];returndata;
}
}#pragmamark 加載圖片
-(void)loadImage:(NSNumber *)index{inti=[index integerValue];
NSData *data= [self requestData:i];
NSThread *currentThread=[NSThread currentThread];// ? ?如果當(dāng)前線程處于取消狀態(tài),則退出當(dāng)前線程if(currentThread.isCancelled) {
NSLog(@"thread(%@) will be cancelled!",currentThread);
[NSThread exit];//取消當(dāng)前線程}
KCImageData *imageData=[[KCImageData alloc]init];
imageData.index=i;
imageData.data=data;
[self performSelectorOnMainThread:@selector(updateImage:) withObject:imageData waitUntilDone:YES];
}#pragmamark 多線程下載圖片
-(void)loadImageWithMultiThread{intcount=ROW_COUNT*COLUMN_COUNT;
_threads=[NSMutableArray arrayWithCapacity:count];//創(chuàng)建多個(gè)線程用于填充圖片for(inti=0; i
NSThread *thread=[[NSThread alloc]initWithTarget:self selector:@selector(loadImage:) object:[NSNumber numberWithInt:i]];
thread.name=[NSString stringWithFormat:@"myThread%i",i];//設(shè)置線程名稱[_threads addObject:thread];
}//循環(huán)啟動(dòng)線程for(inti=0; i
NSThread *thread= _threads[i];
[thread start];
}
}#pragmamark 停止加載圖片
-(void)stopLoadImage{for(inti=0; i
NSThread *thread= _threads[i];//判斷線程是否完成可婶,如果沒(méi)有完成則設(shè)置為取消狀態(tài)
//注意設(shè)置為取消狀態(tài)僅僅是改變了線程狀態(tài)而言沿癞,并不能終止線程if(!thread.isFinished) {
[thread cancel];
}
}
}
@end
運(yùn)行效果(點(diǎn)擊加載大概1秒后點(diǎn)擊停止加載):
使用NSThread在進(jìn)行多線程開發(fā)過(guò)程中操作比較簡(jiǎn)單援雇,但是要控制線程執(zhí)行順序并不容易(前面萬(wàn)不得已采用了休眠的方法)矛渴,另外在這個(gè)過(guò)程中如果打印線程會(huì)發(fā)現(xiàn)循環(huán)幾次就創(chuàng)建了幾個(gè)線程,這在實(shí)際開發(fā)過(guò)程中是不得不考慮的問(wèn)題,因?yàn)槊總€(gè)線程的創(chuàng)建也是相當(dāng)占用系統(tǒng)開銷的具温。
為了簡(jiǎn)化多線程開發(fā)過(guò)程蚕涤,蘋果官方對(duì)NSObject進(jìn)行分類擴(kuò)展(本質(zhì)還是創(chuàng)建NSThread),對(duì)于簡(jiǎn)單的多線程操作可以直接使用這些擴(kuò)展方法铣猩。
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg:在后臺(tái)執(zhí)行一個(gè)操作揖铜,本質(zhì)就是重新創(chuàng)建一個(gè)線程執(zhí)行當(dāng)前方法。
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait:在指定的線程上執(zhí)行一個(gè)方法达皿,需要用戶創(chuàng)建一個(gè)線程對(duì)象天吓。
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait:在主線程上執(zhí)行一個(gè)方法(前面已經(jīng)使用過(guò))。
例如前面加載圖多個(gè)圖片的方法峦椰,可以改為后臺(tái)線程執(zhí)行:
-(void)loadImageWithMultiThread{intcount=ROW_COUNT*COLUMN_COUNT;for(inti=0; i
[self performSelectorInBackground:@selector(loadImage:) withObject:[NSNumber numberWithInt:i]];
}
}