简介
iOS有三种多线程编程的技术分别是NSThread 、Cocoa NSOperation、GCD,抽象度层次是从低到高的,抽象度越高的使用越简单,也是Apple最推荐使用的。在前面呢我对GCD的基本使用做过一些简单的总结,大家有空可以看看。这里呢我主要对NSThread的一些基本的使用呢做一些基本的概括。主要是对自己进行一些总结,也希望对大家使用NSThread的开发者有用。
NSThread相比其他两个的用法呢,最大的差别就是我们要对我们做的事情负责,简单来说就是我们用了它,就得需要自己管理thread的生命周期,线程之间的同步也需要自己手动添加代码,其实呢也没那么麻烦的哈,不要被吓到了。从事过java开发的人来说呢thread就更好理解了,因为他们是完全类似的。是不是已经迫不及待了,那我们就进入正题吧。
启动方式
1、显式启动
1 2 3 4 5 6 7
| //NSThread的创建主要有两种直接方式: [NSThread detachNewThreadSelector:@selector(todoMethod:) toTarget:self withObject:nil]; NSThread* myThread = [[NSThread alloc] initWithTarget:self selector:@selector(todoMethod:) object:nil]; [myThread start];
|
这两种方式的区别是:前一种一调用就会立即创建一个线程来做事情;而后一种虽然你 alloc 了也 init了,但是要直到我们手动调用 start 启动线程时才会真正去创建线程。
这种延迟实现思想在很多跟资源相关的地方都有用到。后一种方式我们还可以在启动线程之前对线程 进行配置比如设置stack大小,给它取个名字啊什么的还有设置线程优先级都是很方便的。
2、隐式启动
1
| [myObj performSelectorInBackground:@selector(myThreadMainMethod) withObject:nil];
|
利用 NSObject 的类方法直接就创建了个线程,是不是更加的方便了,但是他的效果和detachNewThreadSelector:toTarget:withObject:是一样的。
线程通讯
NSThread线程通讯提供了很多的方法,包括获取主线程、子线程之间进行通讯、操作当前线程、取消发送给当前线程的消息等等
这里我只举每个地方使用所用到的几个方法
在应用程序主线程 中做事情:
performSelectorOnMainThread:withObject:waitUntilDone:
performSelectorOnMainThread:withObject:waitUntilDone:modes:
在指定 线程 中做事情:
performSelector:onThread:withObject:waitUntilDone:
performSelector:onThread:withObject:waitUntilDone:modes:
在当前 线程 中做事情:
performSelector:withObject:afterDelay:
performSelector:withObject:afterDelay:inModes:
取消发送给当前 线程 的某个消息
cancelPreviousPerformRequestsWithTarget:
cancelPreviousPerformRequestsWithTarget:selector:object:
eg:[self performSelectorOnMainThread:@selector(updateUI) withObject:nil waitUntilDone:NO]搞定,使用起来很方便。
线程同步
涉及到同步操作呢,学java的同学应该就不陌生了,那就是使用锁。NSThread提供了很多类型的锁NSLock、NSCondition、循环锁NSRecursiveLock,条件锁NSConditionLock,分布式锁NSDistributedLock。其中呢NSLock、NSCondition是比较常用的锁。
看个例子吧[参考 iOS多线程编程之NSThread的使用](http://blog.csdn.net/totogo2010/article/details/8010231)
NSThread的线程同步
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @class ViewController; @interface AppDelegate : UIResponder <UIApplicationDelegate> { int tickets; int count; NSThread* ticketsThreadone; NSThread* ticketsThreadtwo; NSCondition* ticketsCondition; NSLock *theLock; } @property (strong, nonatomic) UIWindow *window; @property (strong, nonatomic) ViewController *viewController; @end
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { tickets = 100; count = 0; theLock = [[NSLock alloc] init]; // 锁对象 ticketsCondition = [[NSCondition alloc] init]; ticketsThreadone = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil]; [ticketsThreadone setName:@"Thread-1"]; [ticketsThreadone start]; ticketsThreadtwo = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil]; [ticketsThreadtwo setName:@"Thread-2"]; [ticketsThreadtwo start]; self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; // Override point for customization after application launch. self.viewController = [[ViewController alloc] initWithNibName:@"ViewController" bundle:nil]; self.window.rootViewController = self.viewController; [self.window makeKeyAndVisible]; return YES; } - (void)run{ while (TRUE) { // 上锁 // [ticketsCondition lock]; [theLock lock]; if(tickets >= 0){ [NSThread sleepForTimeInterval:0.09]; count = 100 - tickets; NSLog(@"当前票数是:%d,售出:%d,线程名:%@",tickets,count,[[NSThread currentThread] name]); tickets--; }else{ break; } [theLock unlock]; // [ticketsCondition unlock]; } }
|
加上锁后呢线程使用就安全了很多,不会出现票被多卖了的情况,加上lock之后线程同步保证了数据的正确性,这里没有要求线程的顺序,所以NSCondition可以去掉。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { tickets = 100; count = 0; theLock = [[NSLock alloc] init]; // 锁对象 ticketsCondition = [[NSCondition alloc] init]; ticketsThreadone = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil]; [ticketsThreadone setName:@"Thread-1"]; [ticketsThreadone start]; ticketsThreadtwo = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil]; [ticketsThreadtwo setName:@"Thread-2"]; [ticketsThreadtwo start]; NSThread *ticketsThreadthree = [[NSThread alloc] initWithTarget:self selector:@selector(run3) object:nil]; [ticketsThreadthree setName:@"Thread-3"]; [ticketsThreadthree start]; self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; // Override point for customization after application launch. self.viewController = [[ViewController alloc] initWithNibName:@"ViewController" bundle:nil]; self.window.rootViewController = self.viewController; [self.window makeKeyAndVisible]; return YES; } -(void)run3{ while (YES) { [ticketsCondition lock]; [NSThread sleepForTimeInterval:3]; [ticketsCondition signal]; [ticketsCondition unlock]; } } - (void)run{ while (TRUE) { // 上锁 [ticketsCondition lock]; [ticketsCondition wait]; [theLock lock]; if(tickets >= 0){ [NSThread sleepForTimeInterval:0.09]; count = 100 - tickets; NSLog(@"当前票数是:%d,售出:%d,线程名:%@",tickets,count,[[NSThread currentThread] name]); tickets--; }else{ break; } [theLock unlock]; [ticketsCondition unlock]; } }
|
[ticketsCondition signal]; 发送信号的方式,在一个线程唤醒另外一个线程的等待。wait是等待,我加了一个 线程3 去唤醒其他两个线程锁中的wait
其实为了简化NSLock的使用我们还可以使用到synchronized 关键字,这个学过java的人来说又是再熟悉不过的了。
1 2 3 4 5 6 7
| - (void)doSomeThing:(id)anObj { @synchronized(anObj) { // to do something } }
|