同一时间,CPU只能处理1条线程,只有1条线程在工作(执行), 多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换), 如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象
但是当线程数量达到一定量时:CPU会在N多线程之间调度,CPU会累死(造成卡顿),消耗大量的CPU资源。每条线程被调度执行的频次会降低(线程的执行效率降低)
多线程的优点:
能适当提高程序的执行效率, 能适当提高资源利用率( CPU 、内存利用率),多个CPU时充分利用资源
多线程的缺点:
开启线程需要占用一定的内存空间(默认情况下,主线程占用 1M ,子线程占用 512KB ),如果开启大量的线程,会占用大量的内存空间,降低程序的性能
线程越多,CPU在调度线程上的开销就越大程序设计更加复杂:比如线程之间的通信、多线程的数据共享
多线程在iOS中的应用:
主线程:显示\刷新UI界面,处理UI事件(比如点击事件、滚动事件、拖拽事件等)
子线程:比较耗时的操作(图像处理、网络请求等)
线程同步:
1、@synchronized(id anObject),会自动对参数对象加锁,保证临界区内的代码线程安全
2、NSLock 使用lock,加锁 unlock,解锁
NSLock *myLock = [[NSLock alloc] init];
[myLock lock];
//do something here
[myLock unlock];
3、 NSRecursiveLock,递归锁, 多次调用不会阻塞已获取该锁的线程。4、NSConditionLock,条件锁,可以设置条件
NSConditionLock *condLock = [[NSConditionLock alloc] initWithCondition:1];
//线程一,生产者
while(true) {
[condLock lockWhenCondition:1];
//生产数据
[condLock unlockWithCondition:2];
}
5、NSDistributedLock,分布锁文件方式实现,可以跨进程用tryLock方法获取锁。用unlock方法释放锁。如果一个获取锁的进程在释放锁之前挂了,那么锁就一直得不到释放了,此时可以通过breakLock强行获取锁。
iOS中常用到的多线程方式:
在iOS中每个进程启动后都会建立一个主线程(UI线程),这个线程是其他线程的父线程。由于在iOS中除了主线程,其他子线程是独立于Cocoa Touch的,所以只有主线程可以更新UI界面(新版iOS中,使用其他线程更新UI可能也能成功,但是不推荐)。iOS中多线程使用并不复杂,关键是如何控制好各个线程的执行顺序、处理好资源竞争问题。常用的多线程开发有三种方式:
1、NSThread
2、NSOperation
3、GCD(Grand Central Dispatch)
先来说说NSThread是这三种里面相对轻量级的,但也是使用起来最负责的,需要自己管理生命周期,线程之间的同步。多个线程对同一数据同时进行访问时,一般做法是在访问之前加锁,这会导致一定的性能开销。创建子线程有两种直接的方法:
1、// 创建子线程
NSThread *thread = [[NSThreadalloc]initWithTarget:selfselector:@selector(buttonAction:)object:nil];
thread.name = @"thread";
// 创建线程之后需要执行start方法开始执行
[thread start];
2、
[NSThreaddetachNewThreadSelector:@selector(myThreadMainMethod:)toTarget:selfwithObject:nil];
// 优点:可以控制本线程本身
// 缺点: 太繁琐
一种间接方法:
// 1. NSObject 自带的方法实现多线程
[selfperformSelectorInBackground:@selector(button:)withObject:nil];
// 优点: 写法简单
// 缺点:没有线程保护 不能对线程细节做处理
NSOperation 有两种方式一种是利用NSOperation的子类:NSInvocationOperation 和 NSBlockOperation。另一种是NSOperationQueue操作队列
1、// NSOperation 是一个任务类 一个对象包括一个任务
// 这个类只能创建子类使用是一个抽象类
// 系统提供两个子类 NSInvocationOperation
NSInvocationOperation *op1 = [[NSInvocationOperationalloc]initWithTarget:selfselector:@selector(buttonAction:)object:nil];
// 在当前线程执行
[op1 start];
NSBlockOperation *op2 = [NSBlockOperationblockOperationWithBlock:^{
[self buttonAction:nil];
}];
[op2 start];
2、NSOperationQueue里可以加入很多个NSOperation, 把NSOperationQueue看作一个线程池,可往线程池中添加操作(NSOperation)到队列中。线程池中的线程可看作消费者,从队列中取走操作,并执行它。
//优点: 封装了线程和任务的分配过程对线程进行了合理的分配的保护 使用较方便
NSOperationQueue *queue = [[NSOperationQueuealloc]init];
NSInvocationOperation *op1 = [[NSInvocationOperationalloc]initWithTarget:selfselector:@selector(buttonAction:)object:nil];
NSInvocationOperation *op2 = [[NSInvocationOperationalloc]initWithTarget:selfselector:@selector(buttonAction:)object:nil];
NSInvocationOperation *op3 = [[NSInvocationOperationalloc]initWithTarget:selfselector:@selector(buttonAction:)object:nil];
// 开始执行任务之前设置队列的最大并发数
[queue setMaxConcurrentOperationCount:2];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
最后说说GCD(Grand Central Dispatch)是苹果公司开发的技术,以优化的应用程序支持多核心处理器和其他的对称多处理系统的系统。这建立在任务并行执行的线程池模式的基础上的。它首次发布在Mac OS X 10.6 ,iOS 4及以上也可用。GCD的工作原理是:让程序平行排队的特定任务,根据可用的处理资源,安排他们在任何可用的处理器核心上执行任务。一个任务可以是一个函数(function)或者是一个block。
GCD中的FIFO队列称为dispatch queue,它可以保证先进来的任务先得到执行。
dispatch queue分为三种:
1、//并行: DISPATCH_QUEUE_CONCURRENT global dispatch queue,可以并发地执行多个任务,但是执行完成的顺序是随机的。
2、//串行: DISPATCH_QUEUE_SERIAL 同时只执行一个任务,通常用于同步访问特定的资源或数据。
3、Main dispatch queue ,全局可用的serial queue,它是在应用程序主线程上执行任务的。
为了避免界面在处理耗时的操作时卡死,比如读取网络数据,IO,数据库读写等,我们会在另外一个线程中处理这些操作,然后通知主线程更新界面。
dispatch_queue_t globlQueue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_async(globlQueue, ^{
// 全局队列请求数据
NSString *str =@"https://ss0.baidu.com/73t1bjeh1BF3odCf/it/u=4041184511,3950868965&fm=96&s=AC88E013939269E34AD9D8530100E0B2";
str = [str stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURLURLWithString:str];
// 在全局队列(子线程)使用同步方法请求数据
NSData *data = [NSDatadataWithContentsOfURL:url];
UIImage *image = [UIImageimageWithData:data];
// 回到主线程做UI刷新
dispatch_async(mainQueue, ^{
self.imageView.image = image;
});
});
当然GCD还有很多其他的用法比如:
//让代码只执行一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"只有一次");
});
// 让代码延迟执行
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4 * NSEC_PER_SEC)),dispatch_get_main_queue(), ^{
});
对于多线程还有很多东西可以讲,希望大家在实际应用中多利用多线程。 原文:TextField