NSThread是基于线程使用,轻量级的多线程编程方法(相对GCD和NSOperation),一个NSThread对象表明一个线程,须要手动管理线程的生命周期,处理线程同步等问题。css
NSThread * newThread = [[NSThread alloc]initWithTarget:self selector:@selector(threadRun) object:nil];
动态方法返回一个新的thread对象,须要调用start方法来启动线程编程
[NSThread detachNewThreadSelector:@selector(threadRun) toTarget:self withObject:nil];
因为静态方法没有返回值,若是须要获取新建立的thread,须要在selector中调用获取当前线程的方法多线程
[newThread start];
[NSThread sleepForTimeInterval:1.0]; (以暂停一秒为例) [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
NSThread的暂停会有阻塞当前线程的效果oop
[newThread cancel];
取消线程并不会立刻中止并退出线程,仅仅只做(线程是否须要退出)状态记录ui
[NSThread exit];
中止方法会当即终止除主线程之外全部线程(不管是否在执行任务)并退出,须要在掌控全部线程状态的状况下调用此方法,不然可能会致使内存问题。spa
[NSThread currentThread];
[NSThread mainThread];
iOS8之前使用线程
[NSThread setThreadPriority:1.0];
这个方法的优先级的数值设置让人困惑,由于你不知道你应该设置多大的值是比较合适的,所以在iOS8以后,threadPriority添加了一句注释:To be deprecated; use qualityOfService below
code
意思就是iOS8之后推荐使用qualityOfService属性,经过量化的优先级枚举值来设置
qualityOfService的枚举值以下:
NSQualityOfServiceUserInteractive:最高优先级,用于用户交互事件
NSQualityOfServiceUserInitiated:次高优先级,用于用户须要立刻执行的事件
NSQualityOfServiceDefault:默认优先级,主线程和没有设置优先级的线程都默认为这个优先级
NSQualityOfServiceUtility:普通优先级,用于普通任务
NSQualityOfServiceBackground:最低优先级,用于不重要的任务orm
好比给线程设置次高优先级:server
[newThread setQualityOfService:NSQualityOfServiceUserInitiated];
经常使用的有三种:
[self performSelector:@selector(threadRun)]; [self performSelector:@selector(threadRun) withObject:nil]; [self performSelector:@selector(threadRun) withObject:nil afterDelay:2.0];
[self performSelectorOnMainThread:@selector(threadRun) withObject:nil waitUntilDone:YES];
注意:更新UI要在主线程中进行
[self performSelector:@selector(threadRun) onThread:newThread withObject:nil waitUntilDone:YES]; //这里指定为某个线程 [self performSelectorInBackground:@selector(threadRun) withObject:nil];//这里指定为后台线程
线程和其余线程可能会共享一些资源,当多个线程同时读写同一份共享资源的时候,可能会引发冲突。线程同步是指是指在必定的时间内只容许某一个线程访问某个资源
iOS实现线程加锁有NSLock和@synchronized两种方式
情景:某演唱会门票发售,在广州和北京均开设窗口进行销售,如下是代码实现
先监听线程退出的通知,以便知道线程何时退出
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(threadExitNotice) name:NSThreadWillExitNotification object:nil];
设置演唱会的门票数量
_ticketCount = 50;
新建两个子线程(表明两个窗口同时销售门票)
NSThread * window1 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil]; window1.name = @"北京售票窗口"; [window1 start]; NSThread * window2 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil]; window2.name = @"广州售票窗口"; [window2 start];
线程启动后,执行saleTicket,执行完毕后就会退出,为了模拟持续售票的过程,咱们须要给它加一个循环
- (void)saleTicket { while (1) { //若是还有票,继续售卖 if (_ticketCount > 0) { _ticketCount --; NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%ld 窗口:%@", _ticketCount, [NSThread currentThread].name]); [NSThread sleepForTimeInterval:0.2]; } //若是已卖完,关闭售票窗口 else { break; } } }
执行结果:
2016-04-06 19:25:36.637 MutiThread[4705:1371666] 剩余票数:9 窗口:广州售票窗口 2016-04-06 19:25:36.637 MutiThread[4705:1371665] 剩余票数:8 窗口:北京售票窗口 2016-04-06 19:25:36.839 MutiThread[4705:1371666] 剩余票数:7 窗口:广州售票窗口 2016-04-06 19:25:36.839 MutiThread[4705:1371665] 剩余票数:7 窗口:北京售票窗口 2016-04-06 19:25:37.045 MutiThread[4705:1371666] 剩余票数:5 窗口:广州售票窗口 2016-04-06 19:25:37.045 MutiThread[4705:1371665] 剩余票数:6 窗口:北京售票窗口 2016-04-06 19:25:37.250 MutiThread[4705:1371665] 剩余票数:4 窗口:北京售票窗口 2016-04-06 19:25:37.250 MutiThread[4705:1371666] 剩余票数:4 窗口:广州售票窗口 2016-04-06 19:25:37.456 MutiThread[4705:1371666] 剩余票数:2 窗口:广州售票窗口 2016-04-06 19:25:37.456 MutiThread[4705:1371665] 剩余票数:3 窗口:北京售票窗口 2016-04-06 19:25:37.661 MutiThread[4705:1371665] 剩余票数:1 窗口:北京售票窗口 2016-04-06 19:25:37.661 MutiThread[4705:1371666] 剩余票数:1 窗口:广州售票窗口 2016-04-06 19:25:37.866 MutiThread[4705:1371665] 剩余票数:0 窗口:北京售票窗口 2016-04-06 19:25:37.867 MutiThread[4705:1371666] <NSThread: 0x7fdc91e289f0>{number = 3, name = 广州售票窗口} Will Exit 2016-04-06 19:25:38.070 MutiThread[4705:1371665] <NSThread: 0x7fdc91e24d60>{number = 2, name = 北京售票窗口} Will Exit
能够看到,票的销售过程当中出现了剩余数量错乱的状况,这就是前面提到的线程同步问题。
售票是一个典型的须要线程同步的场景,因为售票渠道有不少,而票的资源是有限的,当多个渠道在短期内卖出大量的票的时候,若是没有同步机制来管理票的数量,将会致使票的总数和售出票数对应不上的错误。
咱们在售票的过程当中给票加上同步锁:同一时间内,只有一个线程能对票的数量进行操做,当操做完成以后,其余线程才能继续对票的数量进行操做。
- (void)saleTicket { while (1) { @synchronized(self) { //若是还有票,继续售卖 if (_ticketCount > 0) { _ticketCount --; NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%ld 窗口:%@", _ticketCount, [NSThread currentThread].name]); [NSThread sleepForTimeInterval:0.2]; } //若是已卖完,关闭售票窗口 else { break; } } } }
运行结果:
2016-04-06 19:31:27.913 MutiThread[4718:1406865] 剩余票数:11 窗口:北京售票窗口 2016-04-06 19:31:28.115 MutiThread[4718:1406866] 剩余票数:10 窗口:广州售票窗口 2016-04-06 19:31:28.317 MutiThread[4718:1406865] 剩余票数:9 窗口:北京售票窗口 2016-04-06 19:31:28.522 MutiThread[4718:1406866] 剩余票数:8 窗口:广州售票窗口 2016-04-06 19:31:28.728 MutiThread[4718:1406865] 剩余票数:7 窗口:北京售票窗口 2016-04-06 19:31:28.929 MutiThread[4718:1406866] 剩余票数:6 窗口:广州售票窗口 2016-04-06 19:31:29.134 MutiThread[4718:1406865] 剩余票数:5 窗口:北京售票窗口 2016-04-06 19:31:29.339 MutiThread[4718:1406866] 剩余票数:4 窗口:广州售票窗口 2016-04-06 19:31:29.545 MutiThread[4718:1406865] 剩余票数:3 窗口:北京售票窗口 2016-04-06 19:31:29.751 MutiThread[4718:1406866] 剩余票数:2 窗口:广州售票窗口 2016-04-06 19:31:29.952 MutiThread[4718:1406865] 剩余票数:1 窗口:北京售票窗口 2016-04-06 19:31:30.158 MutiThread[4718:1406866] 剩余票数:0 窗口:广州售票窗口 2016-04-06 19:31:30.363 MutiThread[4718:1406866] <NSThread: 0x7ff0c1637320>{number = 3, name = 广州售票窗口} Will Exit 2016-04-06 19:31:30.363 MutiThread[4718:1406865] <NSThread: 0x7ff0c1420cb0>{number = 2, name = 北京售票窗口} Will Exit
能够看到,票的数量没有出现错乱的状况。
咱们注意到,线程启动后,执行saleTicket完毕后就立刻退出了,怎样能让线程一直运行呢(窗口一直开放,能够随时指派其卖演唱会的门票的任务),答案就是给线程加上runLoop
``` 先监听线程退出的通知,以便知道线程何时退出 [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(threadExitNotice) name:NSThreadWillExitNotification object:nil]; ```
//设置演唱会的门票数量 _ticketCount = 50;
新建两个子线程(表明两个窗口同时销售门票)
NSThread * window1 = [[NSThread alloc]initWithTarget:self selector:@selector(thread1) object:nil]; [window1 start]; NSThread * window2 = [[NSThread alloc]initWithTarget:self selector:@selector(thread2) object:nil]; [window2 start];
接着咱们给线程建立一个runLoop
- (void)thread1 { [NSThread currentThread].name = @"北京售票窗口"; NSRunLoop * runLoop1 = [NSRunLoop currentRunLoop]; [runLoop1 runUntilDate:[NSDate date]]; //一直运行 } - (void)thread2 { [NSThread currentThread].name = @"广州售票窗口"; NSRunLoop * runLoop2 = [NSRunLoop currentRunLoop]; [runLoop2 runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:10.0]]; //自定义运行时间 }
而后就能够指派任务给线程了,这里咱们让两个线程都执行相同的任务(售票)
[self performSelector:@selector(saleTicket) onThread:window1 withObject:nil waitUntilDone:NO]; [self performSelector:@selector(saleTicket) onThread:window2 withObject:nil waitUntilDone:NO];
运行结果:
2016-04-06 19:43:22.585 MutiThread[4762:1478200] 剩余票数:11 窗口:北京售票窗口 2016-04-06 19:43:22.788 MutiThread[4762:1478201] 剩余票数:10 窗口:广州售票窗口 2016-04-06 19:43:22.993 MutiThread[4762:1478200] 剩余票数:9 窗口:北京售票窗口 2016-04-06 19:43:23.198 MutiThread[4762:1478201] 剩余票数:8 窗口:广州售票窗口 2016-04-06 19:43:23.404 MutiThread[4762:1478200] 剩余票数:7 窗口:北京售票窗口 2016-04-06 19:43:23.609 MutiThread[4762:1478201] 剩余票数:6 窗口:广州售票窗口 2016-04-06 19:43:23.810 MutiThread[4762:1478200] 剩余票数:5 窗口:北京售票窗口 2016-04-06 19:43:24.011 MutiThread[4762:1478201] 剩余票数:4 窗口:广州售票窗口 2016-04-06 19:43:24.216 MutiThread[4762:1478200] 剩余票数:3 窗口:北京售票窗口 2016-04-06 19:43:24.422 MutiThread[4762:1478201] 剩余票数:2 窗口:广州售票窗口 2016-04-06 19:43:24.628 MutiThread[4762:1478200] 剩余票数:1 窗口:北京售票窗口 2016-04-06 19:43:24.833 MutiThread[4762:1478201] 剩余票数:0 窗口:广州售票窗口 2016-04-06 19:43:25.039 MutiThread[4762:1478201] <NSThread: 0x7fe0d3c24360>{number = 3, name = 广州售票窗口} Will Exit
能够看到,当票卖完后,两个线程并无退出,仍在继续运行,当到达指定时间后,线程2退出了,若是须要让线程1退出,须要咱们手动管理。
好比咱们让线程完成任务(售票)后自行退出,能够这样操做
- (void)saleTicket { while (1) { @synchronized(self) { //若是还有票,继续售卖 if (_ticketCount > 0) { _ticketCount --; NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%ld 窗口:%@", _ticketCount, [NSThread currentThread].name]); [NSThread sleepForTimeInterval:0.2]; } //若是已卖完,关闭售票窗口 else { if ([NSThread currentThread].isCancelled) { break; }else { NSLog(@"售卖完毕"); //给当前线程标记为取消状态 [[NSThread currentThread] cancel]; //中止当前线程的runLoop CFRunLoopStop(CFRunLoopGetCurrent()); } } } } }
运行结果:
2016-04-06 20:08:38.287 MutiThread[4927:1577193] 剩余票数:10 窗口:北京售票窗口 2016-04-06 20:08:38.489 MutiThread[4927:1577194] 剩余票数:9 窗口:广州售票窗口 2016-04-06 20:08:38.690 MutiThread[4927:1577193] 剩余票数:8 窗口:北京售票窗口 2016-04-06 20:08:38.892 MutiThread[4927:1577194] 剩余票数:7 窗口:广州售票窗口 2016-04-06 20:08:39.094 MutiThread[4927:1577193] 剩余票数:6 窗口:北京售票窗口 2016-04-06 20:08:39.294 MutiThread[4927:1577194] 剩余票数:5 窗口:广州售票窗口 2016-04-06 20:08:39.499 MutiThread[4927:1577193] 剩余票数:4 窗口:北京售票窗口 2016-04-06 20:08:39.700 MutiThread[4927:1577194] 剩余票数:3 窗口:广州售票窗口 2016-04-06 20:08:39.905 MutiThread[4927:1577193] 剩余票数:2 窗口:北京售票窗口 2016-04-06 20:08:40.106 MutiThread[4927:1577194] 剩余票数:1 窗口:广州售票窗口 2016-04-06 20:08:40.312 MutiThread[4927:1577193] 剩余票数:0 窗口:北京售票窗口 2016-04-06 20:08:40.516 MutiThread[4927:1577194] 售卖完毕 2016-04-06 20:08:40.516 MutiThread[4927:1577193] 售卖完毕 2016-04-06 20:08:40.517 MutiThread[4927:1577193] <NSThread: 0x7fb719d54000>{number = 2, name = 北京售票窗口} Will Exit 2016-04-06 20:08:40.517 MutiThread[4927:1577194] <NSThread: 0x7fb719d552f0>{number = 3, name = 广州售票窗口} Will Exit
若是肯定两个线程都是isCancelled状态,能够调用[NSThread exit]方法来终止线程。
接下来将更新GCD和NSOperation篇