1、简介:
IOS 多线程编程之 NSThread 的使用
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 withOb ject:(id)anArgument
第一个是实例方法,第二个是类方法
[cpp] view plaincopy
1. 1、
[NSThread detachNewThreadSelector:@selector(doSomething:) toTarget:self with Object: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 plaincopy
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 URLWithStrin g:url]];
19. UIImage *image = [[UIImage alloc]initWithData:data];
20. if(image == nil){
21.
22. }else{
23. [self performSelectorOnMainThread:@selector(updateUI:) withObject:im age 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:s elf withObject:kURL];
37. NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@select
or(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 waitUntilDon e:YES];
performSelectorOnMainThread 是 NSObject 的方法,除了可以更新主线程的数据外,还可
以更新其他线程的比如:
用:performSelector:onThread:withObject:waitUntilDone:
运行下载图片:
图片下载下来了。
2.3 线程同步
我们演示一个经典的卖票的例子来讲 NSThread 的线程同步:
.h
[cpp] view plaincopy
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 plaincopy
1. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptio ns:(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:@selec tor(run) object:nil];
10. [ticketsThreadone setName:@"Thread-1"];
11. [ticketsThreadone start];
12.
13.
14. ticketsThreadtwo = [[NSThread alloc] initWithTarget:self selector:@selec tor(run) object:nil];
15. [ticketsThreadtwo setName:@"Thread-2"];
16. [ticketsThreadtwo start];
17.
18. self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bou nds]];
19. // Override point for customization after application launch.
20. self.viewController = [[ViewController alloc] initWithNibName:@"ViewCont roller" 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]; 发送信号的方式,在一个线程唤醒另外一个线程的等待。 比如:
[cpp] view plaincopy
1. #import "AppDelegate.h" |
2. |
3. #import "ViewController.h" |
4. |
5. @implementation AppDelegate |
6. |
7. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptio
ns:(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:@selec tor(run) object:nil];
16. [ticketsThreadone setName:@"Thread-1"];
17. [ticketsThreadone start];
18.
19. ticketsThreadtwo = [[NSThread alloc] initWithTarget:self selector:@selec tor(run) object:nil];
20. [ticketsThreadtwo setName:@"Thread-2"];
21. [ticketsThreadtwo start];
22.
23. NSThread *ticketsThreadthree = [[NSThread alloc] initWithTarget:self sel ector:@selector(run3) object:nil];
24. [ticketsThreadthree setName:@"Thread-3"];
25. [ticketsThreadthree start];
26. self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bou nds]];
27. // Override point for customization after application launch.
28. self.viewController = [[ViewController alloc] initWithNibName:@"ViewCont roller" 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 等等,可以自己看官方文档学习