1. 程式人生 > >GCD實踐——序列佇列/併發佇列與iOS多執行緒詳解

GCD實踐——序列佇列/併發佇列與iOS多執行緒詳解

       GCD(Grand Central Dispatch),是蘋果提供的一個解決多執行緒開發的解決方案。GCD會自動管理執行緒的生命週期(建立執行緒,排程任務,銷燬執行緒),完全不需要我們管理,我們只需要告訴幹什麼就行。同時GCD使用block來進行任務的執行,用起來非常方便、靈活。本篇部落格我們主要將GCD,其他的多執行緒實現方式還有NSThread、NSOperationQueue,我們將會在以後的部落格中講解。

       這裡我們要提到一個概念:【任務】。就是操作,就是你要執行的一段程式碼,在GCD中就是一個Block,所以新增任務十分方便。

【同步執行】:只要是同步執行的任務,都會在當前執行緒執行,不會另開執行緒。所以說網路請求等耗時操作一般不使用同步,而是非同步,否則會阻塞主執行緒,介面會卡住。

【非同步執行】:只要是非同步任務執行的任務,都會另開執行緒,在別的執行緒執行。

同步(sync)和非同步(async)的主要區別在於會不會阻塞當前執行緒,直到Block中的任務執行完畢。     

如果是同步(sync)操作,它會阻塞當前執行緒並等待Block中的任務執行完畢,然後當前執行緒才會繼續往下執行(因為同步操作沒有開新執行緒,是在主執行緒中執行的,所以會阻塞)。

如果是非同步(async)操作,當前執行緒會直接往下執行,他不會阻塞當前執行緒(因為非同步操作是在另一個執行緒中執行的,所以不會阻塞主執行緒)。

佇列:用於存放任務,一共有兩種佇列:序列佇列和並行佇列。

1.序列佇列(private dispatch queue)一次只執行一個執行緒,按照新增到佇列的順序依次執行;

2.並行佇列(global dispatch queue)一次可以執行多個執行緒,執行緒的執行沒有先後順序。根據同步或者非同步有不同的執行方式。放到並行佇列的任務,GCD也會FIFO的取出來,但不同的是,他取出來一個就會放到別的執行緒,然後再取出來一個又放到另一個執行緒。


3.Main dispatch queue:UI介面所在的執行緒佇列是主執行緒。

【序列佇列示例】

(1)首先匯入GCD封裝後的程式碼,可以從我的專案中直接複製,總共9個檔案:其實個人推薦使用iOS原生的GCD,請移步《iOS開發——GCD的使用與多執行緒開發淺析》這篇部落格。

(2)ViewController中的實現如下:

#import "ViewController.h"
#import "GCD.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
  [super viewDidLoad];

  //執行序列佇列;
  [self serailQueue];
  

}

//序列佇列;
- (void)serailQueue{

  //創建出佇列;
  GCDQueue *queue = [[GCDQueue alloc] initSerial];
  
  //執行佇列中的執行緒;
  [queue execute:^{
    
    NSLog(@"1");
    
  }];
  
  
  [queue execute:^{
    
    NSLog(@"2");
    
  }];
  
  
  [queue execute:^{
    
    NSLog(@"3");
    
  }];
  
  
  [queue execute:^{
    
    NSLog(@"4");
    
  }];
  
  
  [queue execute:^{
    
    NSLog(@"5");
    
  }];
  
}

@end

(3)執行結果如下:

(4)結果分析

輸出結果和我們的預期一樣,是按照新增到佇列的順序執行的。

【並行佇列示例】

(1)同樣需要引入GCD原始碼,在ViewController中的實現如下:

#import "ViewController.h"
#import "GCD.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
  [super viewDidLoad];
  
  //執行併發佇列;
  [self concurrent];
  
 
}


//併發佇列;
- (void)concurrent{
  //創建出佇列;
  GCDQueue *queue = [[GCDQueue alloc] initConcurrent];
  
  //執行佇列中的執行緒;
  [queue execute:^{
    
    NSLog(@"1");
    
  }];
  
  
  [queue execute:^{
    
    NSLog(@"2");
    
  }];
  
  
  [queue execute:^{
    
    NSLog(@"3");
    
  }];
  
  
  [queue execute:^{
    
    NSLog(@"4");
    
  }];
  
  
  [queue execute:^{
    
    NSLog(@"5");
    
  }];
  
}



@end

(2)結果輸出:

(3)結果分析:

輸出結果和我們預期的一樣,執行的順序是無序的。

【UI介面更新】

(1)使用GCD,我們可以來進行網路操作或者下載請求,然後更新UI。這一系列操作是序列操作,需要有一定的順序。在這裡我使用了同步請求。在ViewController中實現如下:

#import "ViewController.h"
#import "GCD.h"

@interface ViewController ()

@property(strong,nonatomic) UIImage *image;
@property (weak, nonatomic) IBOutlet UIImageView *imageView;

@end

@implementation ViewController

- (void)viewDidLoad {
  [super viewDidLoad];
  
//globalqueue其實就是並行佇列。
  [GCDQueue executeInGlobalQueue:^{
    
    //處理業務邏輯
    NSString *url = @"http://imgsrc.baidu.com/forum/w%3D580/sign=2e824145d2c8a786be2a4a065708c9c7/5a8e72094b36acaf254077437fd98d1000e99c4a.jpg";
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
    NSData *picData = [NSURLConnection sendSynchronousRequest:request
                                            returningResponse:nil
                                                        error:nil];
    
    NSLog(@"處理業務邏輯");
    
    //獲取圖片;
    self.image = [UIImage imageWithData:picData];
    
    [GCDQueue executeInMainQueue:^{
      
      NSLog(@"更新UI");
      
      //更新UI
      [self.imageView setImage:self.image];
      
      
    }];
    
    
  }];
  
}

@end

(2)注意,我在storyboard中放了一個ImageView控制元件。執行效果如下: