iOS多執行緒程式設計之NSThread的使用

LoveApp_Han發表於2013-09-12

1、簡介:

1.1 iOS有三種多執行緒程式設計的技術,分別是:

1.、NSThread 

2、Cocoa NSOperation (iOS多執行緒程式設計之NSOperation和NSOperationQueue的使用

3、GCD  全稱:Grand Central Dispatch( iOS多執行緒程式設計之Grand Central Dispatch(GCD)介紹和使用

這三種程式設計方式從上到下,抽象度層次是從低到高的,抽象度越高的使用越簡單,也是Apple最推薦使用的。

這篇我們主要介紹和使用NSThread,後面會繼續2、3 的講解和使用。

1.2 三種方式的有缺點介紹:

NSThread:

優點:NSThread 比其他兩個輕量級

缺點:需要自己管理執行緒的生命週期,執行緒同步。執行緒同步對資料的加鎖會有一定的系統開銷

NSThread實現的技術有下面三種:

Technology

Description

Cocoa threads

Cocoa implements threads using the NSThread class. Cocoa also provides methods on NSObject for spawning new threads and executing code on already-running threads. For more information, see “Using NSThread” and “Using NSObject to Spawn a Thread.”

POSIX threads

POSIX threads provide a C-based interface for creating threads. If you are not writing a Cocoa application, this is the best choice for creating threads. The POSIX interface is relatively simple to use and offers ample flexibility for configuring your threads. For more information, see “Using POSIX Threads”

Multiprocessing Services

Multiprocessing Services is a legacy C-based interface used by applications transitioning from older versions of Mac OS. This technology is available in OS X only and should be avoided for any new development. Instead, you should use the NSThread class or POSIX threads. If you need more information on this technology, see Multiprocessing Services Programming Guide.

一般使用cocoa thread 技術。


Cocoa operation 

優點:不需要關心執行緒管理,資料同步的事情,可以把精力放在自己需要執行的操作上。

Cocoa operation 相關的類是 NSOperation ,NSOperationQueue。NSOperation是個抽象類,使用它必須用它的子類,可以實現它或者使用它定義好的兩個子類:NSInvocationOperation 和 NSBlockOperation。建立NSOperation子類的物件,把物件新增到NSOperationQueue佇列裡執行。

GCD

Grand Central Dispatch (GCD)是Apple開發的一個多核程式設計的解決方法。在iOS4.0開始之後才能使用。GCD是一個替代諸如NSThread, NSOperationQueue, NSInvocationOperation等技術的很高效和強大的技術。現在的iOS系統都升級到6了,所以不用擔心該技術不能使用。


介紹完這三種多執行緒程式設計方式,我們這篇先介紹NSThread的使用。

2、NSThread的使用

2.1 NSThread 有兩種直接建立方式:

- (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument

+ (void)detachNewThreadSelector:(SEL)aSelector toTarget:(id)aTarget withObject:(id)anArgument

第一個是例項方法,第二個是類方法

  1. 1、[NSThread detachNewThreadSelector:@selector(doSomething:) toTarget:self withObject:nil];  
  2. 2、NSThread* myThread = [[NSThread alloc] initWithTarget:self  
  3.                                         selector:@selector(doSomething:)  
  4.                                         object:nil];  
  5. [myThread start];  

2.2引數的意義:

selector :執行緒執行的方法,這個selector只能有一個引數,而且不能有返回值。

target  :selector訊息傳送的物件

argument:傳輸給target的唯一引數,也可以是nil

第一種方式會直接建立執行緒並且開始執行執行緒,第二種方式是先建立執行緒物件,然後再執行執行緒操作,在執行執行緒操作前可以設定執行緒的優先順序等執行緒資訊

2.3 PS:不顯式建立執行緒的方法:

用NSObject的類方法  performSelectorInBackground:withObject: 建立一個執行緒:
[Obj performSelectorInBackground:@selector(doSomething) withObject:nil];

2.4 下載圖片的例子:

2.4.1  新建singeView app

新建專案,並在xib檔案上放置一個imageView控制元件。按住control鍵拖到viewControll

er.h檔案中建立imageView IBOutlet 

ViewController.m中實現:

  1. //  
  2. //  ViewController.m  
  3. //  NSThreadDemo  
  4. //  
  5. //  Created by rongfzh on 12-9-23.  
  6. //  Copyright (c) 2012年 rongfzh. All rights reserved.  
  7. //  
  8.   
  9. #import "ViewController.h"  
  10. #define kURL @"http://avatar.csdn.net/2/C/D/1_totogo2010.jpg"  
  11. @interface ViewController ()  
  12.   
  13. @end  
  14.   
  15. @implementation ViewController  
  16.   
  17. -(void)downloadImage:(NSString *) url{  
  18.     NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:url]];  
  19.     UIImage *image = [[UIImage alloc]initWithData:data];  
  20.     if(image == nil){  
  21.           
  22.     }else{  
  23.         [self performSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:YES];  
  24.     }  
  25. }  
  26.   
  27. -(void)updateUI:(UIImage*) image{  
  28.     self.imageView.image = image;  
  29. }  
  30.   
  31.   
  32. - (void)viewDidLoad  
  33. {  
  34.     [super viewDidLoad];  
  35.       
  36. //    [NSThread detachNewThreadSelector:@selector(downloadImage:) toTarget:self withObject:kURL];  
  37.     NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(downloadImage:) object:kURL];  
  38.     [thread start];  
  39. }  
  40.   
  41. - (void)didReceiveMemoryWarning  
  42. {  
  43.     [super didReceiveMemoryWarning];  
  44.     // Dispose of any resources that can be recreated.  
  45. }  
  46.   
  47. @end  

2.4.2執行緒間通訊

執行緒下載完圖片後怎麼通知主執行緒更新介面呢?

[self performSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:YES];

performSelectorOnMainThread是NSObject的方法,除了可以更新主執行緒的資料外,還可以更新其他執行緒的比如:

用:performSelector:onThread:withObject:waitUntilDone: 

執行下載圖片:


圖片下載下來了。

2.3 執行緒同步

我們演示一個經典的賣票的例子來講NSThread的執行緒同步:

.h

  1. #import <UIKit/UIKit.h>  
  2.   
  3. @class ViewController;  
  4.   
  5. @interface AppDelegate : UIResponder <UIApplicationDelegate>  
  6. {  
  7.     int tickets;  
  8.     int count;  
  9.     NSThread* ticketsThreadone;  
  10.     NSThread* ticketsThreadtwo;  
  11.     NSCondition* ticketsCondition;  
  12.     NSLock *theLock;  
  13. }  
  14. @property (strong, nonatomic) UIWindow *window;  
  15.   
  16. @property (strong, nonatomic) ViewController *viewController;  
  17.   
  18. @end  

  1. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions  
  2. {  
  3.       
  4.     tickets = 100;  
  5.     count = 0;  
  6.     theLock = [[NSLock alloc] init];  
  7.     // 鎖物件  
  8.     ticketsCondition = [[NSCondition alloc] init];  
  9.     ticketsThreadone = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];  
  10.     [ticketsThreadone setName:@"Thread-1"];  
  11.     [ticketsThreadone start];  
  12.       
  13.       
  14.     ticketsThreadtwo = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];  
  15.     [ticketsThreadtwo setName:@"Thread-2"];  
  16.     [ticketsThreadtwo start];  
  17.       
  18.     self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];  
  19.     // Override point for customization after application launch.  
  20.     self.viewController = [[ViewController alloc] initWithNibName:@"ViewController" bundle:nil];  
  21.     self.window.rootViewController = self.viewController;  
  22.     [self.window makeKeyAndVisible];  
  23.     return YES;  
  24. }  
  25.   
  26. - (void)run{  
  27.     while (TRUE) {  
  28.         // 上鎖  
  29. //        [ticketsCondition lock];  
  30.         [theLock lock];  
  31.         if(tickets >= 0){  
  32.             [NSThread sleepForTimeInterval:0.09];  
  33.             count = 100 - tickets;  
  34.             NSLog(@"當前票數是:%d,售出:%d,執行緒名:%@",tickets,count,[[NSThread currentThread] name]);  
  35.             tickets--;  
  36.         }else{  
  37.             break;  
  38.         }  
  39.         [theLock unlock];  
  40. //        [ticketsCondition unlock];  
  41.     }  
  42. }  
如果沒有執行緒同步的lock,賣票數可能是-1.加上lock之後執行緒同步保證了資料的正確性。
上面例子我使用了兩種鎖,一種NSCondition ,一種是:NSLock。 NSCondition我已經註釋了。

執行緒的順序執行

他們都可以通過

        [ticketsCondition signal]; 傳送訊號的方式,在一個執行緒喚醒另外一個執行緒的等待。

比如:

  1. #import "AppDelegate.h"  
  2.   
  3. #import "ViewController.h"  
  4.   
  5. @implementation AppDelegate  
  6.   
  7. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions  
  8. {  
  9.       
  10.     tickets = 100;  
  11.     count = 0;  
  12.     theLock = [[NSLock alloc] init];  
  13.     // 鎖物件  
  14.     ticketsCondition = [[NSCondition alloc] init];  
  15.     ticketsThreadone = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];  
  16.     [ticketsThreadone setName:@"Thread-1"];  
  17.     [ticketsThreadone start];  
  18.       
  19.     ticketsThreadtwo = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];  
  20.     [ticketsThreadtwo setName:@"Thread-2"];  
  21.     [ticketsThreadtwo start];  
  22.       
  23.     NSThread *ticketsThreadthree = [[NSThread alloc] initWithTarget:self selector:@selector(run3) object:nil];  
  24.     [ticketsThreadthree setName:@"Thread-3"];  
  25.     [ticketsThreadthree start];      
  26.     self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];  
  27.     // Override point for customization after application launch.  
  28.     self.viewController = [[ViewController alloc] initWithNibName:@"ViewController" bundle:nil];  
  29.     self.window.rootViewController = self.viewController;  
  30.     [self.window makeKeyAndVisible];  
  31.     return YES;  
  32. }  
  33.   
  34. -(void)run3{  
  35.     while (YES) {  
  36.         [ticketsCondition lock];  
  37.         [NSThread sleepForTimeInterval:3];  
  38.         [ticketsCondition signal];  
  39.         [ticketsCondition unlock];  
  40.     }  
  41. }  
  42.   
  43. - (void)run{  
  44.     while (TRUE) {  
  45.         // 上鎖  
  46.         [ticketsCondition lock];  
  47.         [ticketsCondition wait];  
  48.         [theLock lock];  
  49.         if(tickets >= 0){  
  50.             [NSThread sleepForTimeInterval:0.09];  
  51.             count = 100 - tickets;  
  52.             NSLog(@"當前票數是:%d,售出:%d,執行緒名:%@",tickets,count,[[NSThread currentThread] name]);  
  53.             tickets--;  
  54.         }else{  
  55.             break;  
  56.         }  
  57.         [theLock unlock];  
  58.         [ticketsCondition unlock];  
  59.     }  
  60. }  
wait是等待,我加了一個 執行緒3 去喚醒其他兩個執行緒鎖中的wait

其他同步

我們可以使用指令 @synchronized 來簡化 NSLock的使用,這樣我們就不必顯示編寫建立NSLock,加鎖並解鎖相關程式碼。
- (void)doSomeThing:(id)anObj
{
    @synchronized(anObj)
    {
        // Everything between the braces is protected by the @synchronized directive.
    }
}
還有其他的一些鎖物件,比如:迴圈鎖NSRecursiveLock,條件鎖NSConditionLock,分散式鎖NSDistributedLock等等,可以自己看官方文件學習

NSThread下載圖片的例子程式碼:http://download.csdn.net/detail/totogo2010/4591149

著作權宣告:本文由http://blog.csdn.net/totogo2010/原創,歡迎轉載分享。請尊重作者勞動,轉載時保留該宣告和作者部落格連結,謝謝!

相關文章