一個進程要想執行任務,必須得有線程(每1個進程至少要有1條線程)
線程 : 執行任務的單元片段叫做線程,也就是真正的任務執行者,只不過系統默認把任務交給主線程來做. 大多時候為了提高用戶體驗需要把耗時的任務交給子線程 來做.
一個進程是由一個或多個線程組成.進程只負責資源的調度和分配.線程才是 程序的執行單元,負責代碼的執行. 每個正在運行的程序至少包含一個線程(即主線程),該線程在程序啟動時被創建用于執行mian函數
在多線程方法中為保證對象的即使釋放,需要為每個方法手動添加自動釋放池
iOS中關于UI的添加和刷新必須在主線程中操作
//多線程
1. NSthread
優點: NSThread 比其他兩個輕量級
缺點 : 要自己管理線程的生命周期,線程同步.線程同步對數據的加鎖會有系統開銷
2. NSOperation
優點: 不需要關心線程管理,數據同步的事情可以把精力放在自己需要操作的地方
3. GCD
//優點: 集合了替代 NSThread, NSOperationQueue,NSInvocationOperation等的高效強大技術,使用更方便
一 . NSThread
①使用NSThread的 類方法 創建子線程
在viewDidLoad方法創建線程
[NSThread detachNewThreadSelector:@selector(handleNetWorkRequestImage1) toTarget:self withObject:nil];
-(void) handleNetWorkRequestImage1 {
@autoreleasepool {
NSURL *url = [NSURL URLWithString:@"http://g.hiphotos.baidu.com/image/h%3D360/sign=4f75daaec35c10383b7ec8c48210931c/2cf5e0fe9925bc31fa45db2c5bdf8db1cb13706e.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
//主線程跳轉到子線程執行任務時,會直接創建子線程,執行耗時操作時直接使用該方法即可
//當子線程執行完任務后,接下來的界面刷新操作等應交由主線程操作,使用performSelectorOnMainThread:方法操作
//從子線程回到主線程執行任務
[self performSelectorOnMainThread:@selector(refreashUIFirst:) withObject:image waitUntilDone:YES];
}
}
-(void)refreashUIFirst:(UIImage *)image{
self.imageShowFirst.image = image;
}
//② 使用NSThread 對象的--alloc-- init 初始化方法創建子線程
[[[NSThread alloc] initWithTarget:self selector:@selector(handleNetWorkImageRequest2) object:nil] start]
-(void) handleNetWorkImageRequest2{
@autoreleasepool {
NSURL *url = [NSURL URLWithString:@"http://images.enet.com.cn/egames/articleimage/201112/20111208025418685.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
//返回主線程刷新界面
[self performSelectorOnMainThread:@selector(refreashUISecond:) withObject:image waitUntilDone:YES];
}
}
//刷新界面
-(void)refreashUISecond:(UIImage *)image{
self.imageShowSecond.image = image;
}
??注意 : 初始化方法創建子線程時要通過手動開啟 start 和取消 cancel 子線程或者結束線程[NSThread exit]
線程互斥是指某一資源同時只允許一個訪問者對其進行訪問,具有唯一性和排它性。但互斥無法限制訪問者對資源的訪問順序,即訪問是無序的
當多個線程同時訪問一個資源時會產生線程互斥的問題,如何解決NSThread的線程同步互斥問題呢?
(1)加鎖NSLock 或NSCondition (2)使用@Synchronized
****************賣票問題 解決NSThread的線程同步互斥問題********************
創建票數屬性 totalTickets , 在viewDidLoad方法中添加兩個線程
totalTickets = 100;//初始化票的總張數
self.lock = [[NSLock alloc] init];
//窗口1
[NSThread detachNewThreadSelector:@selector(sellTicketsWithName:) toTarget:self withObject:@"張三"];
//窗口2
[NSThread detachNewThreadSelector:@selector(sellTicketsWithName:) toTarget:self withObject:@"李四"];
-(void)sellTicketsWithName:(NSString *)name{
@autoreleasepool {
while (YES) {
[self.lock lock];//線程加鎖
if (totalTickets > 0) { //賣票
[NSThread sleepForTimeInterval:0.09];
totalTickets --;
NSLog(@"%@賣的票,剩余%ld張" ,name,totalTickets);
}else{//沒票
NSLog(@"%@ 票買完了",name);
break;
}
[self.lock unlock];//解鎖
//線程死鎖:臨界資源缺少解鎖,就會造成死鎖,其他線程一致等待前一個線程解鎖
/*
@synchronized(name) {
if (totalTickets > 0) {
//賣票
[NSThread sleepForTimeInterval:0.09];
totalTickets --;
NSLog(@"%@賣的票,剩余%ld張" ,name,totalTickets);
}else{
//沒票
NSLog(@"%@ 票買完了",name);
break;
}
}
*/
}
}
}
二 .NSObject
//創建 異步后臺執行 子線程,使用NSobject提供的方法
[self performSelectorInBackground:@selector(handleImageRequest3) withObject:nil];
-(void) handleImageRequest3{
@autoreleasepool {
NSURL *url = [NSURL URLWithString:@"http://image.tianjimedia.com/uploadImages/2012/243/8RM0WDLRMWNA.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
//從子線程回到主線程執行任務
[self performSelectorOnMainThread:@selector(refreashUIThird:) withObject:image waitUntilDone:YES];
}
}
-(void)refreashUIThird:(UIImage *)image{
self.imageShowThird.image = image;
}
三 .NSOperation
NSOperation 類在MVC中屬于M,是用來封裝單個任務相關的代碼和數據的抽象出來的類, 他只是一個操作沒有主線程,子線程之分,本身與多線程沒有任何關系, 通常不直接使用而是使用其子類(NSInvocationOperation或NSBlockOperation)
使用 操作隊列 NSOperationQueue來 管理一組 Operation對象 ,根據需要為 operation 開辟合適數量的線程 實現任務的并行執行
1.線程同步 : 同步執行, 任務之間存在先后順序,后一任務在前一任務完成后才執行
/* 線程同步存在兩種方法:
第一種 : 設置線程并發數
第二種 : 設置多任務的依賴關系
*/
2.線程并發 : 異步執行(不需要設置并發數),任務之間沒有先后順序,先執行的可能最后結束
(3.1) NSInvocationOperation 封裝了執行操作的target和要執行的action
NSInvocationOperation *operation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(handleNetWorkRequestImage1) object:nil];
NSInvocationOperation *operation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(handleNetWorkImageRequest2) object:nil];
//創建任務隊列,來操作任務的執行
//① 設置線程并發數為1 ,實現同步執行
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue setMaxConcurrentOperationCount:1];
[queue addOperation:operation1];
[queue addOperation:operation2];
(3.2) NSBlockOperation 封裝了要執行的代碼塊
NSBlockOperation * operation3 = [NSBlockOperation blockOperationWithBlock:^{
[self handleImageRequest3];
}];
NSBlockOperation *operation4 = [NSBlockOperation blockOperationWithBlock:^{
[self handleImageRequestFromNet4];
}];
//網絡請求圖片
-(void) handleImageRequest3{
@autoreleasepool {
NSURL *url = [NSURL URLWithString:@"http://image.tianjimedia.com/uploadImages/2012/243/8RM0WDLRMWNA.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
//從子線程回到主線程執行任務
[self performSelectorOnMainThread:@selector(refreashUIThird:) withObject:image waitUntilDone:YES];
}
}
-(void) handleImageRequestFromNet4{
@autoreleasepool {
NSURL *url = [NSURL URLWithString:@"http://f.hiphotos.baidu.com/image/h%3D200/sign=a4d0f7d46409c93d18f209f7af3ff8bb/024f78f0f736afc314f682bfb019ebc4b6451275.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
[self performSelectorOnMainThread:@selector(refreashUIFourth:) withObject:image waitUntilDone:YES];
}
}
//刷新界面
-(void)refreashUIThird:(UIImage *)image{
self.imageShowThird.image = image;
}
-(void)refreashUIFourth:(UIImage *)image{
self.imageShowFourth.image = image;
}
四 .GCD處理多線程
GCD的工作原理是:讓程序平行排隊的特定任務,根據可用的處理資源,安排他們在任何可用的處理器核心上執行任務。任務可以是一個函數(function)或者是一個block。 GCD的底層依然是用線程實現,不過這樣可以讓程序員不用關注實現的細節。
GCD完全可以處理諸如 數據鎖定和資源泄漏等復雜的異步編程問題
//串行隊列
- (IBAction)chuanXing:(UIButton *)sender {
//1.創建串行隊列
//(1)獲取系統創建好的串行隊列,在主線程中實現線程同步
dispatch_queue_t queue1 = dispatch_get_main_queue();
//(2)自己創建串行隊列,任務在子線程是線程同步
//參數一 : 隊列名稱 也是隊列的唯一標示,蘋果建議采用反域名形式編寫
//參數二 : 指定為什么類型的隊列
//DISPATCH_QUEUE_SERIAL ------指定為串行隊列
dispatch_queue_t queue2 = dispatch_queue_create("com.lanou3g.www", DISPATCH_QUEUE_SERIAL);
//2.往隊列中添加任務
dispatch_async(queue2, ^{
NSLog(@"任務一%@",[NSThread currentThread]);
});
dispatch_async(queue2, ^{
NSLog(@"任務二%@",[NSThread currentThread]);
});
dispatch_async(queue2, ^{
NSLog(@"任務三%@",[NSThread currentThread]);
});
dispatch_async(queue2, ^{
NSLog(@"任務四%@",[NSThread currentThread]);
});
//釋放 ------MRC
// dispatch_release(queue2);
}
//并行隊列
- (IBAction)bingXing:(UIButton*)sender {
//1.創建并行隊列
//(1)使用系統創建好的并行隊列
//參數一 : 優先級 系統提供四種
//參數二 : 預留參數 現在未使用 給 0 即可
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//(2)自己創建并發隊列
dispatch_queue_t queue3 = dispatch_queue_create("com.lanou3g.www", DISPATCH_QUEUE_CONCURRENT);
//往隊列中添加任務
dispatch_async(queue3, ^{
NSLog(@"任務一%@",[NSThread currentThread]);
});
dispatch_async(queue3, ^{
NSLog(@"任務二%@",[NSThread currentThread]);
});
dispatch_async(queue3, ^{
NSLog(@"任務三%@",[NSThread currentThread]);
});
dispatch_async(queue3, ^{
NSLog(@"任務四%@",[NSThread currentThread]);
//請求到數據后要回到主線程刷新界面
dispatch_async(dispatch_get_main_queue(), ^{
//此處寫想在主線程中執行的代碼段 --- 比如:刷新UI界面等操作
});
});
//MRC 時釋放
// dispatch_release(queue3);
}
//分組隊列
- (IBAction)fenZu:(UIButton *)sender {
//1.創建并行隊列
dispatch_queue_t queue4 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//2. 創建分組
dispatch_group_t group = dispatch_group_create();
//3.往分組隊列添加任務
dispatch_group_async(group, queue4, ^{
NSLog(@"任務一 ,請求0-10M的數據");
});
dispatch_group_async(group, queue4, ^{
NSLog(@"任務二 ,請求10-20M的數據");
});
dispatch_group_async(group, queue4, ^{
NSLog(@"任務三 ,請求20-30M的數據");
});
dispatch_group_async(group, queue4, ^{
NSLog(@"任務四 ,請求20-40M的數據");
});
//當分組所有任務完成后出發的方法
dispatch_group_notify(group, queue4, ^{
//數據拼接
NSLog(@"數據拼接");
});
//MRC 時釋放
// dispatch_release(group);
}
//一次
- (IBAction)Once:(UIButton *)sender {
}
//障礙隊列
- (IBAction)zhangAi:(UIButton *)sender {
//障礙任務的作用 : 可以保證障礙之后的并發的任務在障礙之后并發的任務執行完畢之后去執行
//注意: 如果添加障礙任務必須使用自己創建的并發隊列
//1.創建并發隊列
dispatch_queue_t quee = dispatch_queue_create("com.lanou.henan", DISPATCH_QUEUE_CONCURRENT);
//2.往隊列中添加任務
dispatch_async(quee, ^{
NSLog(@" A 寫入");
});
dispatch_async(quee, ^{
NSLog(@" B 寫入");
});
dispatch_async(quee, ^{
NSLog(@" C 寫入");
});
dispatch_async(quee, ^{
NSLog(@" D 寫入");
});
//添加障礙任務
dispatch_barrier_async(quee, ^{
NSLog(@"此處是坑,障礙");
});
dispatch_async(quee, ^{
NSLog(@" A 讀取");
});
dispatch_async(quee, ^{
NSLog(@" B 讀取");
});
dispatch_async(quee, ^{
NSLog(@" C 讀取");
});
dispatch_async(quee, ^{
NSLog(@" D 讀取");
});
//MRC ---釋放
//dispatch_release(quee);
}
//延遲
- (IBAction)yanChi:(UIButton *)sender {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"蠻王好持久啊啊啊!");
});
//dispatch_get_main_queue()在主線程中執行 如果想在子線程執行, 此處改為子線程即可
}
//重復執行
- (IBAction)chongFu:(UIButton *)sender {
dispatch_queue_t quuee = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(10, quuee, ^(size_t index) {
NSLog(@"反復執行的次數 %ld , 當前線程 %@",index,[NSThread currentThread]);
//注意: size_t之后手寫上參數名 比如 index
//重復的任務在執行的過程中至少一次是在主隊列中
});
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}