NSThread对象
一个NSThread对象就是一条线程。 一般的使用方式:
NSThread* thread = [[NSThread alloc]initWithTarget:self selector:@selector(timeConsumingOperation) object:nil];
[thread start];
[NSThread detachNewThreadSelector:@selector(timeConsumingOperation) toTarget:self withObject:nil];
[self performSelectorInBackground:@selector(timeConsumingOperation) withObject:nil];
NSThread对象的一些属性
name
name属性一般都要认真去填写,因为多线程比较复杂,在大型项目中,一旦出错的话,能凭借name来定位到错误的代码。 一般采用的是逆序全程域名的命名规范,例如:
@"com.apple.development.nsthread.init"
threadPriority(优先级)
Double类型,0.0~1.0之间,默认是0.5。
NSThread的阻塞or暂停
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:5]];
NSThread的退出
[NSThread exit];
一旦调用exit方法,也就意味着销毁线程,线程就不能继续执行后续的任务了,即便是后续还有相关的代码安排,因为这些执行指令是在线程中执行的,而线程已经不存在了,那相关的后续指令也就不会执行了。
多线程的安全隐患
多个线程中访问同一个资源/变量,造成同一个资源/变量在不同的线程中具有不同的内容/值。 解决方式:互斥锁
@synchronized (token) {
}
大多数用在多个线程用同一套代码,比如:
NSThread* threadA = [[NSThread alloc]initWithTarget:self selector:@selector(timeConsumingOperation) object:nil];
NSThread* threadB = [[NSThread alloc]initWithTarget:self selector:@selector(timeConsumingOperation) object:nil];
此处声明了两个线程threadA和threadB,他们都同样执行的是timeConsumingOperation方法,所以有可能两个线程会在timeConsumingOperation方法中引发一些资源争夺方面的问题。解决方法是在timeConsumingOperation方法中,将有关资源争夺的相关代码用 @synchronized 圈起来,这样就能保证在同一时刻,只有一条线程执行 @synchronized 互斥锁中的代码,这样就解决了资源争夺的问题。 如果token参数是timeConsumingOperation方法中的一个临时(局部)变量,则因为是在timeConsumingOperation方法中创建和销毁的,所以访问不到其他线程中的被锁代码段,所以局部变量是锁不住的。此处的token若为self,则因为是一个对象内部的全局变量,则在该对象下创建的线程皆可以被访问到,因而自然可以访问到每个每个线程中的被锁代码段。从而确保被锁代码段同一时间只有一个线程可以允许访问。 互斥锁的实现原理实际上就是运用了线程同步的技术,即多条线程按顺序执行任务,这样势必会造成一些线程陷入等待。 缺点: 会消耗大量的CPU资源。
要遵守的问题:
- 加锁的代码尽可能短小精简。
- token参数是要为可以访问被锁线程的参数。
苹果并不推荐用这个方式来防止多线程中的资源争夺。
原子属性
@interface text()
@property (nonatomic,strong)NSObject* objA;
@property (atomic,strong)NSObject* objB;
@end
只要将成员变量声明为原子属性的变量,那么相当于在setter方法中的赋值操作加上了互斥锁。 原子属性解决了单线程写、多线程读的情况下的资源争夺问题。 但原子属性虽然线程安全,但是会消耗大量的资源,我们在开发中应尽量避免资源争夺的设计,将加锁、资源争夺的业务逻辑交给服务器端处理,以减少客户端的压力。
线程间通信
一般我们将耗时的操作都放到其他线程去做。 苹果官方建议,与UI界面刷新等相关的操作最好都放到主线程中进行。 因此,一般情况下,我们在子线程处理完耗时操作后,通常都需要反馈到主线程进行界面的刷新、响应等操作。 这里就涉及到了子线程与主线程之间的通信问题,在子线程执行完后通知主线程进行刷新或者响应等一系列的操作。
[self performSelectorInBackground:@selector(timeConsumingOperation) withObject:nil];
- (void)timeConsumingOperation{
[self performSelectorOnMainThread:@selector(updateInterface) withObject:nil waitUntilDone:NO];
}
withObject: 后传的是前面updateInterface方法需要传的参数,此处不需要参数,所以为nil waitUntilDone: 后传的是布尔值,意思是,是否需要等待主线程方法updateInterface执行完毕再继续原timeConsumingOperation方法中剩余代码段的执行,若是更新界面的代码与timeConsumingOperation后续的业务代码无关,这里一般传NO。
|