首先我们得知道,cpu仅能同时处理一条线程,多线程并发并不是多条线程同时进行,而是cpu不断在线程间切换进行,所以线程并不是越多越好,当存在大量线程,会让cpu在切换间疲于奔命,反而不利于开发。
具体来说,iOS方面多线程也就是两种,pthread以及NSThread。pthread是C语言写的多线程,好处是不仅仅用在iOS移动端开发,基本上支持C语言的都可以使用,缺点就是C语言的共性了,不易识别,难记而且并不支持arc。所以在iOS中多线程开发还是去了解NSThread更好,pthread掌握即可。
在oc对象的使用中,最为常见的就是alloc与init,对象内存分配和实例化。所以就有可能出现这种情况:
NSThread *thread1 = [[NSThread alloc] init];
[thread1 start];
实际上这是不行的,NSThread无法直接实例化使用,但是可以派生子类来实例化。要使用线程对象,可以通过这种方式:
NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(runThread) object:nil];
[thread2 start];
当然如果不想使用对象方式开启线程,有更简便的方式:
[NSThread detachNewThreadSelector:@selector(runThread) toTarget:self withObject:nil];
当然,这种方式就没办法使用线程的一些属性了。可以用thread2.name 来给线程自定义一个名字,这样当出现线程崩溃的情况,可以在左边的崩溃列表直接定位到是哪个线程出现了问题。另外可以通过threadPriority来设置线程的优先级,不过设置优先级仅仅增加了线程的被调用概率,而不是完全先调用优先级高的线程。
可以看到,虽然优先调用了thread2,但是其中仍然穿插了thread3的调用。(在多线程开发中,不要相信一次或者少数次运行结果);
线程的状态包括五种,nsthread可以帮我们管理其中的四种,一种是在线程池中创建一个线程,第二种是[thread start],这并不是直接开启线程,而是让线程进入准备状态,runloop可以随时调用,第三种就是runing 了,这个是我们无法控制的,第四个是sleep状态或者叫阻塞状态,当线程运行完或我们调用sleep就会进行这种状态,第五种就是dead 了,可以通过exit来杀死线程。
进一步学习,我们使用线程是用来干什么的呢?当然是耗时操作不方便放在主线程种调用。那么我们处理完数据后,还是要去修改UI,难道是直接修改就可以了?当然不是,iOSUI使用都是在主线程中,所有就有了线程间通讯技术使用,保证安全准确更新UI
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
第一个SEL是在主线程种执行的方法,第二个arg是需要传入的参数,第三个wait是表示当前线程是否等待,如果为YES,那么只有等待sel方法执行完了,线程才会继续进行。
在子线程中,或许你可以更新UI成功,但是这会出现很多不确定性问题,譬如你今天运行OK,明天再次运行就会挂了,让你完全摸不着头脑。
提到上面这个方法,就必定了解到另外一个
[self performSelector:@selector(othreRunThread) onThread:thread2 withObject:nil waitUntilDone:NO];
你会发现,当你提交函数进入thread2中,函数并没有被执行。这就涉及到runloop,runloop会开启一个死循环,让线程的执行形成一个圆,当执行完毕就会从头开始再次执行,不断循环询问系统是否有方法需要执行,我们开启的子线程并没有加入循环中,就像一条线,它已经走到了底部,你再在线的起点加入函数,线程也不会回头去执行。我们可以在线程中使用实现runloop
- (void)runThread{
[[NSRunLoop currentRunLoop] run];
NSLog(@"------%@,%@-------",NSStringFromSelector(_cmd),[NSThread currentThread]);
}
不过这并不是一个很好的选择,这样会开启一个始终占用资源的死循环线程,我们并没有很好的办法去停止这个循环。
- (void)runThread{
while (!self.isfinshed) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1f]];
}
NSLog(@"------%@,%@-------",NSStringFromSelector(_cmd),[NSThread currentThread]);
}
- (void)othreRunThread{
self.finshed = YES;
NSLog(@"------%@,%@-------",NSStringFromSelector(_cmd),[NSThread currentThread]);
}
我们可以用一个全局的判断条件来进行设置,当othreRunThread未被执行的时候,我们就开启循环0.1秒,不断的让线程去询问是否有方法未被执行,当方法已经执行后,就不再进行循环。