iOS多線程編程之NSThread的使用

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線程間通訊

線程下載完圖片後怎麼通知主線程更新界面呢?

[selfperformSelectorOnMainThread:@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/原創,歡迎轉載分享。請尊重作者勞動,轉載時保留該聲明和作者博客鏈接,謝謝!


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章