NSThread 子線程 Cocoa NSOperation GCD(Grand Central Dispatch) 多線程

單詞:thread 英 θred:n 線、思路、vt 穿過、vi 穿透過

1、    進程、線程數據庫

進程:正在進行中的程序被稱爲進程,負責程序運行的內存分配,每個進程都有本身獨立的虛擬內存空間編程

線程:線程是進程中一個獨立的執行路徑(控制單元),一個進程中至少包含一條線程,即主線程(UI操做),能夠將耗時的執行路徑(如網絡請求)放在其餘線程中執行,線程不能被殺掉,可是能夠暫停/休眠一條線程。網絡

建立線程的目的:開啓一條新的執行路徑,運行指定的代碼,與主線程中的代碼實現同時運行多線程

多線程的優點:充分發揮多核處理器優點,將不一樣線程任務分配給不一樣的處理器,真正進入"並行運算"狀態;將耗時的任務分配到其餘線程執行,由主線程負責統一更新界面會使應用程序更加流暢,用戶體驗更好;當硬件處理器的數量增長,程序會運行更快,而程序無需作任何調整.。併發

弊端:新建線程會消耗內存空間和CPU時間,線程太多會下降系統的運行性能app

2、    NSThread異步

優勢:NSThread 比其餘兩個輕量級async

缺點:須要本身管理線程的生命週期,線程同步。線程同步對數據的加鎖會有必定的系統開銷oop

一、獲取當前線程
NSLog(@"%@",[NSThread currentThread]);//獲取當前線程,當打印的number等於1時,表明當前是主線程,大於1表明子線程
二、建立子線程方式(兩種方法)
1)顯式建立方式,須要手動開啓線程。 最後的object表明線程觸發方法時傳遞的參數,注意,有且只有一個參數
thread = [[NSThreadalloc] initWithTarget:selfselector:@selector(run1:) object:@"123"];
    thread.name = @"123";//給線程起個名字
    [thread start];//開啓線程
2)隱式建立方式,不須要手動開啓
[NSThread detachNewThreadSelector:@selector(run2) toTarget:self withObject:nil];

[self performSelectorInBackground:@selector(test:) withObject:@"456"];性能

三、線程間通信

回到主線程:    [self performSelectorOnMainThread:@selector(testMain:) withObject:nil waitUntilDone:YES];// waitUntilDone是指是否等到主線程把方法執行完了,這個performSelector方法才返回。

在指定線程上執行操做:[self performSelector:@selector(run) onThread:thread withObject:nil waitUntilDone:YES];

在本線程執行:    [self performSelector:@selector(run) withObject:nil];

四、實現方法
1) [NSThread sleepForTimeInterval:3];//讓調用該方法的線程睡一會,參數表明秒數
2)  NSRunLoop *r = [NSRunLoop currentRunLoop];//獲取當前線程的runloop
3)  [self performSelectorOnMainThread:@selector(test) withObject:nil waitUntilDone:NO];//跳入主線程的方法,第一個參數表明要調用的方法,第二個參數表明傳的值,第三個參數表明是否等待此操做完成後再繼續往下執行
4) [self performSelector:@selector(test) withObject:nil];//在當前線程執行另一個方法
5) [self performSelector:@selector(test) withObject:nil afterDelay:3];//延遲調用
6) NSDate *date = [NSDate dateWithTimeInterval:2 sinceDate:[NSDate date]];

     [NSThread sleepUntilDate:date];//以當前時間爲標準,延遲到某個時間再執行該線程操做

7) 獲取主線程:    NSThread *main = [NSThread mainThread];

8) 獲取當前線程:    NSThread *current = [NSThread currentThread];

9) 結束線程:[NSThread exit];//當前線程方法中使用

     [thread cancel];//主線程中使用

五、具體代碼以下:
#import "ViewController.h"
@interface ViewController (){
    NSThread *thread;
    BOOL    _isEnd;//線程終止的標識
}
@end
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    _isEnd = NO;
    NSLog(@"%@",[NSThread currentThread]);//獲取當前線程,當打印的number等於1時,表明當前是主線程,大於1表明子線程
    //*子線程建立方式1: 最後的object表明線程觸發方法時傳遞的參數,注意,有且只有一個參數
    thread = [[NSThread alloc] initWithTarget:self selector:@selector(run1:) object:@"123"];
    thread.name = @"123";//給線程起個名字
    [thread start];//開啓線程,只有顯式的建立方式,須要手動開啓、
    //*子線程建立方式2:
    [NSThread detachNewThreadSelector:@selector(run2) toTarget:self withObject:nil];//隱式建立,不須要手動開啓
}
-(void)run1:(NSString *)str{
   // [NSThread sleepForTimeInterval:3];//讓調用該方法的線程睡一會,參數表明秒數
    NSLog(@"子線程:%@",[NSThread currentThread]);
    NSRunLoop *r = [NSRunLoop currentRunLoop];//獲取當前線程的runloop
    //判斷當前線程是否須要循環(線程繼續存活)
    while (!_isEnd) {
        [r runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];//runMode 第一個參數表明消息方式,默認寫NSDefaultRunLoopMode,第二個參數表明結束時間,通常給distantFuture,表明不可達到的將來某個時間
    }
    NSLog(@"銷燬");
}
-(void)run2{
    NSLog(@"子線程2:%@",[NSThread currentThread]);
//*    [self performSelectorOnMainThread:@selector(test) withObject:nil waitUntilDone:NO];//跳入主線程的方法,第一個參數表明要調用的方法,第二個參數表明傳的值,第三個參數表明是否等待此操做完成後再繼續往下執行
   
 //*   [self performSelector:@selector(test) withObject:nil];//在當前線程執行另一個方法
 //*延遲調用    [self performSelector:<#(nonnull SEL)#> withObject:<#(nullable id)#> afterDelay:<#(NSTimeInterval)#>] 
    [self performSelector:@selector(run3) onThread:thread withObject:nil waitUntilDone:NO];//在某個線程中執行某個方法 
    NSLog(@"其餘");
}
-(void)run3{
    _isEnd = YES;//方法執行結束後將線程結束標識置爲YES
    NSLog(@"-----");
}
-(void)test{
    NSLog(@"主線程方法%@",[NSThread currentThread]);
}
 

3、    Cocoa NSOperation

優勢:不須要關心線程管理,數據同步的事情,能夠把精力放在本身須要執行的操做上。

NSOperation實例封裝了須要執行的操做和執行操做所需的數據,而且可以以併發或非併發的方式執行這個操做。

NSOperation自己是抽象基類,所以必須使用它的子類,使用NSOperation子類的方式有2種:一種是用定義好的兩個子類:NSInvocationOperation 和 NSBlockOperation,另外一種是自定義子類繼承NSOperation,實現內部相應的方法

一、       NSOperation的做用:配合使用NSOperation和NSOperationQueue也能實現多線程編程取消操做

二、       NSOperation和NSOperationQueue實現多線程的具體步驟:

先將須要執行的操做封裝到一個NSOperation對象中

而後將NSOperation對象添加到NSOperationQueue中

系統會⾃動將NSOperationQueue中的NSOperation取出來

將取出的NSOperation封裝的操做放到⼀條新線程中執⾏

二、建立操做對象,封裝要執行的任務
//NSInvocationOperation   封裝操做
NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run1) object:nil];
 
NSInvocationOperation *operation1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run1) object:nil];
三、執行操做
    [operation start];
    [operation1 start];
注意:操做對象默認在主線程中執行,只有添加到隊列中才會開啓新的線程。即默認狀況下,若是操做沒有放到隊列queue中,都是同步執行。只有將NSOperation放到一個
四、NSBlockOperation建立操做對象
    NSBlockOperation *block = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"block操做:%@",[NSThread currentThread]);
    }];
    //在NSBlockOperation對象中添加一個操做,若是NSBlockOperation對象包含了多個操做,有一個是在主線中一行,其餘均在子線程中執行
五、添加操做(注意添加要放在start以前)
    [block addExecutionBlock:^{
        NSLog(@"block操做2:%@",[NSThread currentThread]);
    }];
    [block addExecutionBlock:^{
       NSLog(@"block操做3:%@",[NSThread currentThread]);
    }];
    [block addExecutionBlock:^{
        NSLog(@"block操做4:%@",[NSThread currentThread]);
    }];
    六、執行操做
  [block start ];//注意添加要放在start以前
   
    七、NSOperationQueue:建立隊列:將操做放入隊列(主隊列除外),默認在子線程中執行,且不須要手動start
1)NSOperationQueue的做⽤:NSOperation能夠調⽤start⽅法來執⾏任務,但默認是同步執行的
若是將NSOperation添加到NSOperationQueue(操做隊列)中,系統會自動異步執行NSOperation中的操做
添加操做到NSOperationQueue中,自動執行操做,自動開啓線程
2)建立NSOperationQueue 
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    NSInvocationOperation *ioperation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run2) object:nil];
八、把操做添加到隊列中
   1)第一種:添加以前建立的
  [queue addOperation:ioperation];
   2)第二種:添加的時候建立
注意:使用NSOperationQueue時,無需start
  [queue addOperationWithBlock:^{
        NSLog(@"block操做隊列:%@",[NSThread currentThread]);
    }];
九、事件的實現
-(void)run1{
    NSLog(@"執行操做:%@",[NSThread currentThread]);
}
-(void)run2{
    NSLog(@"隊列中執行:%@",[NSThread currentThread]);
}
 
 
4、GCD 全稱:Grand Central Dispatch
一、Grand Central Dispatch (GCD)是Apple開發的一個多核編程的解決方法。在iOS4.0開始以後才能使用。GCD是一個替代諸如NSThread, NSOperationQueue, NSInvocationOperation等技術的很高效和強大的技術。
二、GCD的工做原理是:讓程序平行排隊的特定任務,根據可用的處理資源,安排他們在任何可用的處理器核心上執行任務
三、 GCD是基於C語言的。若是使用GCD,徹底由系統管理線程,咱們不須要編寫線程代碼。只需定義想要執行的任務,而後添加到適當的調度隊列(dispatch queue)。GCD會負責建立線程和調度你的任務,系統直接提供線程管理
四、經常使用的方法dispatch_async
爲了不界面在處理耗時的操做時卡死,好比讀取網絡數據,IO,數據庫讀寫等,咱們會在另一個線程中處理這些操做,而後通知主線程更新界面。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 耗時的操做
        dispatch_async(dispatch_get_main_queue(), ^{
            // 更新界面
        });
});
dispatch_async開啓一個異步操做,第一個參數是指定一個gcd隊列,第二個參數是分配一個處理事物的程序塊到該隊列。
dispatch_get_global_queue(0, 0),指用了全局隊列。
通常來講系統自己會有3個隊列:global_queue、current_queue(廢棄)和main_queue
獲取一個全局隊列是接受兩個參數,第一個是我分配的事物處理程序塊隊列優先級。分高低和默認,0爲默認2爲高,-2爲低
五、dispatch_group_async的使用
dispatch_group_async能夠實現監聽一組任務是否完成,完成後獲得通知執行其餘的操做。這個方法頗有用,好比你執行三個下載任務,當三個任務都下載完成後你才通知界面說完成了。
1)建立一個組
    dispatch_group_t group = dispatch_group_create();
2)在一個組內提交一個代碼塊來執行。必須明確這個代碼塊屬於哪一個組,必須     在哪一個派送隊列上執行。
dispatch_group_async(group, queue, ^{
       NSLog(@"group1--%@",[NSThread currentThread]);
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"group2--%@",[NSThread currentThread]);    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"group3--%@",[NSThread currentThread]);    });
3)等待組中的任務執行完畢,回到主線程執行block回調
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"updateUi");
    });
六、dispatch_barrier_async
dispatch_barrier_async是在前面的任務執行結束後它才執行,並且它後面的任務等它執行完成以後纔會執行
     //建立隊列,第一個參數表明隊列名,第二個表明串行仍是並行:DISPATCH_QUEUE_SERIAL串行   DISPATCH_QUEUE_CONCURRENT 並行
  dispatch_queue_t queue = dispatch_queue_create("123", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        NSLog(@"dispatch_async1");
});
//上一步執行結束才執行這一步
    dispatch_barrier_async(queue, ^{
        NSLog(@"dispatch_barrier_async");
});
//執行完上一步才執行下一步
    dispatch_async(queue, ^{
        NSLog(@"dispatch_async3");
    });
七、dispatch_apply 
     它通常都是放在dispatch_async裏面(異步)。
執行某個代碼片斷N次
  dispatch_async(dispatch_get_global_queue(0, 0), ^{
        dispatch_apply(5, dispatch_get_global_queue(0, 0), ^(size_t index) {
            NSLog(@"%ld",index);
        }); 
    });
 
 
八、具體代碼
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    UIImageView *imageview = [[UIImageView alloc]initWithFrame:CGRectMake(10, 30, 100, 100)];
    [self.view addSubview:imageview];
    UIImageView *imageview1 = [[UIImageView alloc]initWithFrame:CGRectMake(30, 150, 100, 100)];
    [self.view addSubview:imageview1];
   UIImageView *imageview2 = [[UIImageView alloc]initWithFrame:CGRectMake(30, 270, 100, 100)];
    [self.view addSubview:imageview2];
    UIImageView *imageview3 = [[UIImageView alloc]initWithFrame:CGRectMake(30, 390, 100, 100)];
    [self.view addSubview:imageview3];
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        //NSLog(@"耗時操做:%@",[NSThread currentThread]);   
        NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@" http://www.yooyoo360.com/photo/2009-1-1/20090112132752467.jpg"]];
        UIImage *image = [UIImage imageWithData:data];
        //NSLog(@"---%@",image);
        //返回主線程
        dispatch_async(dispatch_get_main_queue(), ^{
            //NSLog(@"更新UI:%@",[NSThread currentThread]);
            imageview.image = image;
        });   
    });
    //組
    //建立組,用於存放耗時操做
    dispatch_group_t group = dispatch_group_create();  
    __block UIImage *image,*image1,*image2,*image3;
    //將操做封裝進組,第一個參數表明要存放操做的組名,第二個參數表明操做隊列,block執行耗時操做
//在一個組內提交一個代碼塊來執行。必須明確這個代碼塊屬於哪一個組,必須在哪一個派送隊列上執行。
    dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
        NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@" http://pic2.ooopic.com/10/81/58/62bOOOPICce.jpg"]];
        image1 = [UIImage imageWithData:data];
        //NSLog(@"==%@",image1);
    });
    dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
        NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@" http://www.downsc.com/vector_pic/shiliang_iecool/5/3/b_img/14430.jpg"]];
        image2 = [UIImage imageWithData:data];
    });
    dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
        NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@" http://www.downsc.com/vector_pic/shiliang_iecool/5/2/b_img/13788.jpg"]]; 
        image3 = [UIImage imageWithData:data];
        //NSLog(@"----%@",image); 
    });
    //監聽一組操做,第一個參數表明要監聽的組名,第二個參數表明一組操做徹底結束後跳轉到哪一個隊列,通常跳到主線程(dispatch_get_main_queue),block執行要進行的操做(通常用來更新UI),注意:此方法在組中的全部操做執行完畢後調用
//等待組中的任務執行完畢,回到主線程執行block回調
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        imageview.image = image;
        imageview1.image = image1;
        imageview2.image = image2;
        imageview3.image = image3;
    });
}
-(void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
@end
相關文章
相關標籤/搜索