iOS 多线程之NSThread简单使用
1、线程的构建和开启:
(1)_thread1 = [[NSThread alloc]initWithTarget:self selector:@selector(threadOneMethod) object:nil]; _thread2 = [[NSThread alloc]initWithTarget:self selector:@selector(threadTwoMethod) object:nil]; [_thread1 start]; [_thread2 start];先构建,然后开启,好处是可以控制开启的时间,并且可以得到NSThread对象,便于之后和这个线程通讯。
(2) [NSThread detachNewThreadSelector:@selector(threadOneMethod) toTarget:self withObject:nil]; (3) [self performSelectorInBackground:@selector(threadOneMethod) withObject:nil];还有一种是从系统的NSThread继承一个子类,这个子类的构建和父类一样,但是线程内执行的代码是写在-(void )main方法里面的:
@interface MyThread : NSThread //从NSThread继承 @end //////////////////// -(void )main{ for (int i = 0; i<10; i++) { NSLog(@"myThread count: %d",i); sleep(1); } NSLog(@"thread end"); } // 使用自定义的线程对象 _thread3 = [[MyThread alloc]init]; [_thread3 start];相对而言,前面3个方法构建的线程,线程内执行的方法都是在当前的类文件里面的,而使用main函数,线程中执行的方法是属于线程对象本身的,这样可以在任何其他需要使用这个线程方法的地方使用,而不用在实现一次方法。
2、线程同步,即线程锁的使用:
(1)@synchronized关键词:
-(void )threadOneMethod{ @synchronized(@"lock"){ for (int i= 0; i<10; i++) { //循环10次,每次输出一个标识,然后线程沉睡一秒 NSLog(@"111"); sleep(1); } } NSLog(@"thread1 end"); } /////////////////////// -(void )threadTwoMethod{ @synchronized(@"lock"){ for (int i= 0; i<5; i++) { //循环5次,每次输出一个标识222,然后线程沉睡1秒 NSLog(@"222"); sleep(1); } } NSLog(@"thread2 end"); }上面的例子是结合第一种线程构建方法的,两个线程几乎同时开启,但是第一个更快一些,执行后效果是,第一个方法循环输出10次“111”结束后,第二个方法才输出“222”。因为第一个方法开始进入@synchronized(@"lock")里面之后,第二个方法在执行到@synchronized(@"lock")这里就进不去了,会等到前面一个线程从里面出来,它才能继续向下进行,这就是线程锁的作用。有些时候多个线程会同时操作用同一个数据或对象,可能会出现意想不到的糟糕情况,会使用线程锁让某个线程在操作的时候,其他线程无法操作,从而保证线程安全。
@synchronized(@"lock")是使用括号里面的对象来区分的,如果把第二个方法里的对象改掉,变成@synchronized(@"111111"),就不会起到锁的作用了,因为这是它们就是两个完全不相关的锁了。
2、NSLock:
[_lock lock]; for (int i= 0; i<10; i++) { NSLog(@"111"); sleep(1); } [_lock unlock];核心的方法部分没有区别,只是把锁换成了[_lock lock]……[_lock unlock];这里的锁是依靠_lock这个NSLock对象来识别的,就是说到某个NSLock对象调用了lock方法后,其他线程使用同一个NSLock对象调用lock方法就会停滞不前进,知道同一个NSLock对象再调用unlock方法。
这个过程有点像有些体检项目,房间里同时只能体检一个人,一个人进去后就会把门锁上,这时有其他人来了也只能等着,等到里面的人好了出来,门打开,下一个才能进去。
3、线程间通讯:
副线程给主线程发消息:
[self performSelectorOnMainThread:@selector(mainThreadMethod) withObject:nil waitUntilDone:NO];
self是方法的调用者,mainThreadMethod是让主线程调用的方法,然后这个方法可以带参数,也就是object,最后的waitUntilDone是指是否等到主线程把方法执行完了,这个performSelector方法才返回。
向其他任意线程发消息:[self performSelector:@selector(timerFire) onThread:_thread1 withObject:nil waitUntilDone:NO];主要是这里要传入一个NSThread对象用于指定在哪个线程执行,这也是前面构建线程的时候保留线程对象的意义。
然后,需要注意的是,这些performSelector形式的方法,都需要对方线程的RunLoop处于开启状态,因为这些方法实质是runloop的输入源,把消息发送给对方线程的runloop,然后对方从runloop里面获取消息,才去执行方法。主线程的runloop是默认开启的,副线程的runloop是默认构建,但是需要手动开启。
4、线程的关闭:
需要关闭某个线程,可以给这个线程发消息,使他关闭:
- (IBAction)killThread:(UIButton *)sender { [self performSelector:@selector(exitThread:) onThread:_thread1 withObject:_thread1 waitUntilDone:NO]; } //////////////// -(void)exitThread:(NSThread *)thread{ NSLog(@"thread EXIT1"); [NSThread exit]; NSLog(@"thread EXIT2"); }也可以:
<span style="font-size:14px;">-(void )threadOneMethod{ //前面写线程需要执行任务的代码,最后进入runloop循环,保持线程不结束同时保持接受消息 NSRunLoop *theRL = [NSRunLoop currentRunLoop]; while (shouldKeepRunning ){//通过</span><span style="font-family: Arial, Helvetica, sans-serif;"><span style="font-size:12px;">shouldKeepRunning判断是否继续进行循环,如果为NO,就会停止循环,然后继续向下运行,线程自然结束</span></span><span style="font-size:14px;"> NSLog(@"looprun"); [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; }; NSLog(@"thread1 end"); } /////////// BOOL shouldKeepRunning = YES;//一个全局的BOOL类型变量 - (IBAction)killThread:(UIButton *)sender {//点击按钮,修改</span><span style="font-family: Arial, Helvetica, sans-serif;"><span style="font-size:12px;">shouldKeepRunning为NO</span></span><span style="font-size:14px;"> shouldKeepRunning = NO; }</span>这里是通过一个全局变量的改变来控制线程的继续还是结束。但是有个小问题是,当线程的runloop接受了外来的输入源之后,例如其他线程调用:
[self performSelector:@selector(timerFire) onThread:_thread1 withObject:nil waitUntilDone:NO];在这个线程运行,runloop接受到消息后会阻塞在方法[theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]里面,也即是说while循环不会继续向下一个循环进行,那么改变shouldKeepRunning就不能马上得到反馈,所以需要使用:
BOOL shouldKeepRunning = YES; - (IBAction)killThread:(UIButton *)sender { [self performSelector:@selector(exitThread:) onThread:_thread1 withObject:nil waitUntilDone:NO]; } /////////结合 -(void)exitThread:(NSThread *)thread{ shouldKeepRunning = NO; }这样就是给要关闭的线程发消息,会立刻唤醒目标线程的runloop,因为[theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]方法的特性是在接触到输入源后方法立刻返回,这样while循环就会立刻进入进入下一个循环,也就会进行循环条件的判断,然后因为shouldKeepRunning变为NO了,就会退出循环,然后线程结束。
两种线程结束的方法,貌似第二种更自然些,具体对于线程内部的运行和内存的管理上的影响,还得研究下。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。