iOS多線程編程技術NSThread; NSOperation、GCD三者使用詳解

三種方式的優缺點介紹:
1)NSThread

優勢:NSThread 比其餘兩個輕量級
缺點:須要本身管理線程的生命週期,線程同步。線程同步對數據的加鎖會有必定的系統開銷

2)Cocoa  NSOperation
優勢:不須要關心線程管理, 數據同步的事情,能夠把精力放在本身須要執行的操做上。
Cocoa operation相關的類是NSOperation, NSOperationQueue.
NSOperation是個抽象類,使用它必須用它的子類,能夠實現它或者使用它定義好的兩個子類: NSInvocationOperation和NSBlockOperation.
建立NSOperation子類的對象,把對象添加到NSOperationQueue隊列裏執行。

3) GCD(全優勢)

Grand Central dispatch(GCD)是Apple開發的一個多核編程的解決方案。在iOS4.0開始以後才能使用。GCD是一個替代NSThread, NSOperationQueue,NSInvocationOperation等技術的很高效強大的技術。 html

下面我用簡單易於理解的代碼實例來介紹NSThread在開發實踐中的使用,具體使用時能夠根據實際狀況進行擴展:
1、NSThread的使用(基本已過期) java

[html] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. #import <UIKit/UIKit.h>  
  2.   
  3. #define kURL @"http://www.iyi8.com/uploadfile/2014/0506/20140506085929652.jpg"  
  4.   
  5. @interface ViewController : UIViewController  
  6.   
  7. @end  

[html] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. #import "ViewController.h"  
  2.   
  3. @interface ViewController ()  
  4. {  
  5.     UIImage         *_image;  
  6.     UIImageView     *_imageView;  
  7.       
  8.     int             _tickets;  
  9.     int             _count;  
  10.       
  11.     NSThread        *_threadOne;  
  12.     NSThread        *_threadTwo;  
  13.     NSThread        *_threadThree;  
  14.       
  15.     NSCondition     *_condition;  
  16.       
  17.     NSLock          *_lock;  
  18. }  
  19. @end  
  20.   
  21. @implementation ViewController  
  22.   
  23. - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil  
  24. {  
  25.     self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];  
  26.     if (self) {  
  27.     }  
  28.     return self;  
  29. }  

[html] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. - (void)loadView  
  2. {  
  3.     self.view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];  
  4.     NSLog(@"klfaklfa ------ - - ");  
  5.       
  6.     _imageView = [[UIImageView alloc] initWithFrame:CGRectMake(100, 100, 100, 150)];  
  7.     [self.view addSubview:_imageView];  
  8. }  

/** 測試NSthread使用*/
[html] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. - (void)viewDidLoad  
  2. {  
  3.     [super viewDidLoad];  
  4.       
  5.     _tickets    = 200;  
  6.     _count      = 0;  
  7.     _lock       = [[NSLock alloc] init];  
  8.       
  9.     //鎖對象  
  10.     _condition = [[NSCondition alloc] init];  
  11.     //線程1  
  12.     _threadOne = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];  
  13.     _threadOne.name = @"thread-1";  
  14.     [_threadOne start];  
  15.       
  16.     //線程2  
  17.     _threadTwo = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];  
  18.     _threadTwo.name = @"thread-2";  
  19.     [_threadTwo start];  
  20.       
  21.     //線程3  
  22.     _threadThree = [[NSThread alloc] initWithTarget:self selector:@selector(run3) object:nil];  
  23.     _threadThree.name = @"thread-3";  
  24.     [_threadThree start];  
  25.       
  26.     //若是沒有線程同步的lock,物品售出數量就會出現重複致使數據競爭不一樣步問題.加上lock以後線程同步保證了數據的正確性。  
  27.     [NSThread detachNewThreadSelector:@selector(downloadImage:) toTarget:self withObject:kURL];  
  28.     NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(downloadImage:) object:kURL];  
  29.     [thread start];  
  30. }     

/** 測試NSOperationQueue使用 程序員

使用 NSOperation的方式有兩種,
一種是用定義好的兩個子類:NSInvocationOperation 和 NSBlockOperation。
另外一種是繼承NSOperation
 
若是你也熟悉Java,NSOperation就和java.lang.Runnable接口很類似。和Java的Runnable一 樣,NSOperation也是設計用來擴展的,只需繼承重寫NSOperation的一個方法main。至關與java 中Runnalbe的Run方法。而後把NSOperation子類的對象放入NSOperationQueue隊列中,該隊列就會啓動並開始處理它。
 
NSInvocationOperation例子:
這裏一樣,咱們實現一個下載圖片的例子。新建一個Single View app,拖放一個ImageView控件到xib界面。
實現代碼以下

*/ 數據庫

[html] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. - (void)viewDidLoad  
  2. {  
  3.     [super viewDidLoad];  
  4.       
  5.     NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self  
  6.     selector:@selector(downloadImage:)  
  7.      object:kURL];  
  8.        
  9.      NSOperationQueue *queue = [[NSOperationQueue alloc] init];  
  10.      [queue addOperation:operation];  
  11. }     
第二種方式繼承NSOperation 
在.m文件中實現main方法,main方法編寫要執行的代碼便可。
 
如何控制線程池中的線程數?
隊列裏能夠加入不少個NSOperation, 能夠把NSOperationQueue看做一個線程池,可往線程池中添加操做(NSOperation)到隊列中。線程池中的線程可看做消費者,從隊列中取走操做,並執行它。
 
經過下面的代碼設置:
 
  1. [queue setMaxConcurrentOperationCount:5]; 
 
線程池中的線程數,也就是併發操做數。默認狀況下是-1,-1表示沒有限制,這樣會同時運行隊列中的所有的操做。
 

/** 測試GCD的dispatch_async使用*/ 編程

[html] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. - (void)viewDidLoad  
  2. {  
  3.     [super viewDidLoad];  
  4.       
  5.      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{  
  6.      NSURL * url = [NSURL URLWithString:<span>kURL</span>];  
  7.      NSData * data = [[NSData alloc]initWithContentsOfURL:url];  
  8.      UIImage *image = [[UIImage alloc]initWithData:data];  
  9.           
  10.          if (data != nil) {  
  11.              dispatch_async(dispatch_get_main_queue(), ^{  
  12.              _imageView.image = image;  
  13.             });  
  14.          }  
  15.      });  
  16. }   


(三)GCD的介紹和使用
介紹:
Grand Central Dispatch 簡稱(GCD)是蘋果公司開發的技術,以優化的應用程序支持多核心處理器和其餘的對稱多處理系統的系統。這創建在任務並行執行的線程池模式的基礎上的。它首次發佈在Mac OS X 10.6 ,iOS 4及以上也可用。
 
設計:
GCD的工做原理是:讓程序平行排隊的特定任務,根據可用的處理資源,安排他們在任何可用的處理器核心上執行任務。
 
一個任務能夠是一個函數(function)或者是一個block。 GCD的底層依然是用線程實現,不過這樣可讓程序員不用關注實現的細節。
 
GCD中的FIFO隊列稱爲dispatch queue,它能夠保證先進來的任務先獲得執行。
 
dispatch queue分爲下面三種:
Serial     
又稱爲private dispatch queues,同時只執行一個任務。Serial queue一般用於同步訪問特定的資源或數據。當你建立多個Serial queue時,雖然它們各自是同步執行的,但Serial queue與Serial queue之間是併發執行的。
 
Concurrent
又稱爲global dispatch queue,能夠併發地執行多個任務,可是執行完成的順序是隨機的。
 
Main dispatch queue
它是全局可用的serial queue,它是在應用程序主線程上執行任務的。
 
咱們看看dispatch queue如何使用?
 
一、經常使用的方法dispatch_async
爲了不界面在處理耗時的操做時卡死,好比讀取網絡數據,IO,數據庫讀寫等,咱們會在另一個線程中處理這些操做,而後通知主線程更新界面。
 
用GCD實現這個流程的操做比前面介紹的NSThread  NSOperation的方法都要簡單。代碼框架結構以下

/** 測試GCD的dispatch_group_async使用*/ 網絡

[html] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. - (void)viewDidLoad  
  2. {  
  3.     [super viewDidLoad];  
  4.       
  5.     //此方法能夠實現監聽一組任務是否完成,若是完成後通知其餘操做(如界面更新),此方法在下載附件時挺有用,  
  6.      //在搪行幾個下載任務時,當下載完成後經過dispatch_group_notify通知主線程下載完成並更新相應界面  
  7.      dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);  
  8.      dispatch_group_t group = dispatch_group_create();  
  9.      dispatch_group_async(group, queue, ^{  
  10.          [NSThread sleepForTimeInterval:0.09];  
  11.        
  12.      NSLog(@"group1");  
  13.      NSURL * url = [NSURL URLWithString:kURL];  
  14.      NSData * data = [[NSData alloc]initWithContentsOfURL:url];  
  15.      _image = [[UIImage alloc]initWithData:data];  
  16.        
  17.      });  
  18.      dispatch_group_async(group, queue, ^{  
  19.          [NSThread sleepForTimeInterval:0.09];  
  20.          NSLog(@"group2");  
  21.      });  
  22.      dispatch_group_async(group, queue, ^{  
  23.          [NSThread sleepForTimeInterval:0.09];  
  24.          NSLog(@"group3");  
  25.      });  
  26.        
  27.      dispatch_group_notify(group, dispatch_get_main_queue(), ^{  
  28.          NSLog(@"updateUi");  
  29.        
  30.          _imageView.image = _image;  
  31.      });  
  32. }   

/** 測試GCD的dispatch_barrier_async使用*/ 併發

[html] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. - (void)viewDidLoad  
  2. {  
  3.     [super viewDidLoad];  
  4.       
  5.     //是在前面的任務執行結束後它才執行,並且它後面的任務等它執行完成以後纔會執行  
  6.     dispatch_queue_t queue = dispatch_queue_create("gcd.devdiy.com", DISPATCH_QUEUE_CONCURRENT);  
  7.     dispatch_async(queue, ^{  
  8.         [NSThread sleepForTimeInterval:2];  
  9.         NSLog(@"dispatch_async1");  
  10.     });  
  11.       
  12.     dispatch_async(queue, ^{  
  13.         [NSThread sleepForTimeInterval:4];  
  14.         NSLog(@"dispatch_async2");  
  15.     });  
  16.       
  17.     dispatch_barrier_async(queue, ^{  
  18.         NSLog(@"dispatch_barrier_async");  
  19.         [NSThread sleepForTimeInterval:4];  
  20.     });  
  21.       
  22.     dispatch_async(queue, ^{  
  23.         [NSThread sleepForTimeInterval:1];  
  24.         NSLog(@"dispatch_async");  
  25.     });  
  26. }   

[html] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. - (void)run3  
  2. {  
  3.     while (true) {  
  4.         [_condition lock];  
  5.         [NSThread sleepForTimeInterval:3];  
  6.         NSLog(@"當前物品名稱:%d,售出數量:%d,線程名-=-==: %@", _tickets, _count, [[NSThread currentThread] name]);  
  7.           
  8.         [_condition signal];  
  9.         [_condition unlock];  
  10.     }  
  11. }  
  12.   
  13. - (void)run  
  14. {  
  15.     while (true) {  
  16.         //上鎖  
  17.         [_lock lock];  
  18.         if (_tickets > 0) {  
  19.             [NSThread sleepForTimeInterval:0.09];  
  20.             _count = 200 - _tickets;  
  21.             NSLog(@"當前物品名稱:%d,售出數量:%d,線程名: %@", _tickets, _count, [[NSThread currentThread] name]);  
  22.             _tickets--;  
  23.         }else{  
  24.             break;  
  25.         }  
  26.           
  27.         [_lock unlock];  
  28.     }  
  29. }  
  30.   
  31. /**  
  32.  * @param string url  
  33.  */  
  34. - (void)downloadImage:(NSString *) url{  
  35.     NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:url]];  
  36.     UIImage *image = [[UIImage alloc] initWithData:data];  
  37.     if(image == nil){  
  38.         [self updateUI:image];  
  39.     }else{  
  40.         [self performSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:YES];  
  41.     }  
  42. }  
  43.   
  44. - (void)updateUI:(UIImage *)image  
  45. {  
  46.     _imageView.image = image;  
  47. }  
  48.   
  49. @end  
是否是代碼比NSThread  NSOperation簡潔不少,並且GCD會自動根據任務在多核處理器上分配資源,優化程序。
系統給每個應用程序提供了三個concurrent dispatch queues。這三個併發調度隊列是全局的,它們只有優先級的不一樣。由於是全局的,咱們不須要去建立。咱們只須要經過使用函數dispath_get_global_queue去獲得隊列。
相關文章
相關標籤/搜索