0
点赞
收藏
分享

微信扫一扫

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

第一个是实例方法,第二个是类方法

 



[cpp]  ​​view plain​​ ​​copy​​



  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中实现:



[cpp]  ​​view plain​​ ​​copy​​



  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 
  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. 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: 

运行下载图片:

iOS多线程编程之NSThread的使用_操作系统

图片下载下来了。

2.3 线程同步

我们演示一个经典的卖票的例子来讲NSThread的线程同步:

.h

 



[cpp]  ​​view plain​​ ​​copy​​



  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  



 

 



[cpp]  ​​view plain​​ ​​copy​​



  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. "Thread-1"];  
  11.     [ticketsThreadone start];  
  12.       
  13.       
  14.     ticketsThreadtwo = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];  
  15. "Thread-2"];  
  16.     [ticketsThreadtwo start];  
  17.       
  18.     self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];  
  19. // Override point for customization after application launch.  
  20. "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. "当前票数是:%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]; 发送信号的方式,在一个线程唤醒另外一个线程的等待。

比如:

 



[cpp]  ​​view plain​​ ​​copy​​



  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. "Thread-1"];  
  17.     [ticketsThreadone start];  
  18.       
  19.     ticketsThreadtwo = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];  
  20. "Thread-2"];  
  21.     [ticketsThreadtwo start];  
  22.       
  23.     NSThread *ticketsThreadthree = [[NSThread alloc] initWithTarget:self selector:@selector(run3) object:nil];  
  24. "Thread-3"];  
  25.     [ticketsThreadthree start];      
  26.     self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];  
  27. // Override point for customization after application launch.  
  28. "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. "当前票数是:%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等等,可以自己看官方文档学习


举报

相关推荐

0 条评论