多线程网络总结
一、多线程
1.线程:一个程序就是一个进程,一个进程想要执行任务,必须要有线程,线程是进程执行的基本单元,一个进程的所有任务都在线程上执行。
2.多线程:一个进程可以开启多个线程,每个线程可以并行执行不同的任务。
3.多线程原理:同一个时间,CPU职能执行一条线程,多线程同时执行,其实是CPU在快速的在多条线程之间切换,就造成了多线程的假想。
4.多线程优缺点:
优点:提高程序执行的效率,能适当提高资源的利用率。
缺点:开启线程需要占用一定的内存空间,如果开启大量的线程,会占用大量的空间。线程越多,CPU在调度线程上开销就越大。会使程序设计更加复杂,比如多线程之间的通信,多线程的数据共享。
5.线程的作用:更新UI界面(主线程),处理用户触摸事件。
6.开发中实现多线程的方案:
(1)C语言pthread:一套纯C语言的通用API,且线程的生命周期需要程序员自己管理,使用难度较大,通常不使用。
特点:POSIX线程具有很好的可移植性,使用pthreads编写的代码可运行于Solaris、FreeBSD、Linux 等平台,Windows平台亦有pthreads-win32可供使用。Pthreads定义了一套C语言的,它以pthread.h头文件和一个线程库实现。
数据类型:
pthread_t:线程ID
(2)OC的NSThread
特点:
优点:NSThread较轻量级,更直观地控制线程对象
缺点:需要自己管理线程的生命周期,线程同步。线程同步对数据的加锁会有一定的系统开销
初始化:
1.动态方法
- (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument;
// 初始化线程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
// 设置线程的优先级(0.0 - 1.0,1.0最高级)
thread.threadPriority = 1;
// 开启线程
[thread start];
//参数解析:
selector :线程执行的方法,这个selector最多只能接收一个参数
target :selector消息发送的对象
argument : 传给selector的唯一参数,也可以是nil
2.静态方法
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument;
[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil]; // 调用完毕后,会马上创建并开启新线程
3.隐式创建线程
[self performSelectorInBackground:@selector(run) withObject:nil];
获取当前线程:
NSThread *current = [NSThread currentThread];
获取主线程:
NSThread *main = [NSThread mainThread];
暂停当前线程:
// 暂停2s
[NSThread sleepForTimeInterval:2];
// 或者
NSDate *date = [NSDate dateWithTimeInterval:2 sinceDate:[NSDate date]];
[NSThread sleepUntilDate:date];
线程间通信:
1.在指定线程上执行操作
[self performSelector:@selector(run) onThread:thread withObject:nil waitUntilDone:YES];
2.在主线程上执行操作
[self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:YES];
3.在当前线程执行操作
[self performSelector:@selector(run) withObject:nil];
(3)C语言的GCD接口(性能最好,代码更精简)
特点:
(1)GCD存在于libdispatch.dylib这个库中,这个调度库包含了GCD的所有的东西,任何IOS程序,默认就加载了这个库,在程序运行的过程中会动态的加载这个库,不需要我们手动导入。
(2)GCD是纯C语言的,因此我们在编写GCD相关代码的时候,面对的函数,而不是方法。
(3)GCD中的函数大多数都以dispatch开头。
任务和队列:
GCD中有2个核心概念:(1)任务:执行什么操作 (2)队列:用来存放任务
GCD的使用就2个步骤(1)定制任务 (2)确定想做的事情
将任务添加到队列中,GCD会自动将队列中的任务取出,放到对应的线程中执行
提示:任务的取出遵循队列的FIFO原则:先进先出,后进后出
执行任务:
1.GCD中有2个用来执行任务的函数
说明:把右边的参数(任务)提交给左边的参数(队列)进行执行。
(1)用同步的方式执行任务 dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
参数说明:
queue:队列
block:任务
(2)用异步的方式执行任务 dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
2.同步和异步的区别
同步:在当前线程中执行
异步:在另一条线程中执行
队列:
1.队列的类型
GCD的队列可以分为2大类型
(1)并发队列(Concurrent Dispatch Queue)
可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)并发功能只有在异步(dispatch_async)函数下才有效
(2)串行队列(Serial Dispatch Queue)
让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)
2.补充说明
有4个术语比较容易混淆:同步、异步、并发、串行
同步和异步决定了要不要开启新的线程
同步:在当前线程中执行任务,不具备开启新线程的能力
异步:在新的线程中执行任务,具备开启新线程的能力
并发和串行决定了任务的执行方式
并发:多个任务并发(同时)执行
串行:一个任务执行完毕后,再执行下一个任务
3.串行队列
GCD中获得串行有2种途径
(1)使用dispatch_queue_create函数创建串行队列
dispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr); // 队列名称, 队列属性,一般用NULL即可
示例:
dispatch_queue_t queue = dispatch_queue_create("wendingding", NULL); // 创建
dispatch_release(queue); // 非ARC需要释放手动创建的队列
(2)使用主队列(跟主线程相关联的队列)
主队列是GCD自带的一种特殊的串行队列,放在主队列中的任务,都会放到主线程中执行
使用dispatch_get_main_queue()获得主队列
示例:
dispatch_queue_t queue = dispatch_get_main_queue();
4.并发队列
GCD默认已经提供了全局的并发队列,供整个应用使用,不需要手动创建
使用dispatch_get_global_queue函数获得全局的并发队列
dispatch_queue_t dispatch_get_global_queue(dispatch_queue_priority_t priority,unsigned long flags); // 此参数暂时无用,用0即可
示例:
这个参数是留给以后用的,暂时用不上,传个0。
第一个参数为优先级,这里选择默认的。获取一个全局的默认优先级的并发队列。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
说明:全局并发队列的优先级
#define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默认(中)
#define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后台
5.各种队列的执行效果
线程间通信:
//1)GCD:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 下载图片
UIImage *image = nil;
dispatch_async(dispatch_get_main_queue(), ^{
// 回到主线程
});
});
(4)OC的NSOperation和NSOperationQueue(基于GCD)
简介:
NSOperation的作⽤:配合使用NSOperation和NSOperationQueue也能实现多线程编程
NSOperation和NSOperationQueue实现多线程的具体步骤:
(1)先将需要执行的操作封装到一个NSOperation对象中
(2)然后将NSOperation对象添加到NSOperationQueue中
(3)系统会⾃动将NSOperationQueue中的NSOperation取出来
(4)将取出的NSOperation封装的操作放到⼀条新线程中执⾏
子类:
NSOperation是个抽象类,并不具备封装操作的能力,必须使⽤它的子类
使用NSOperation⼦类的方式有3种:
(1) NSInvocationOperation
NSInvocationOperation *operation=[[NSInvocationOperation alloc]initWithTarget:self selector:@selector(test) object:nil];
//执行操作
[operation start];
操作对象默认在主线程中执行,只有添加到队列中才会开启新的线程。即默认情况下,如果操作没有放到队列中queue中,都是同步执行。只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作 。
(2) NSBlockOperation
NSBlockOperation *operation=[NSBlockOperation blockOperationWithBlock:^{
//......
}];
//添加操作
[operation addExecutionBlock:^{
//....
}];
//执行操作
[operation start];
只要NSBlockOperation封装的操作数 > 1,就会异步执行操作。
(3) 自定义子类继承NSOperation,实现内部相应的⽅法
3. NSOperationQueue的作⽤:NSOperation可以调⽤start⽅法来执⾏任务,但默认是同步执行的
如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作
添加操作到NSOperationQueue中,自动执行操作,自动开启线程。
线程间通信:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 下载图片
UIImage *image = nil;
[self performSelector:@selector(settingImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES modes:nil];
}
//这种情况 也适用于子线程之间的通信。
7.NSOperationQueue和GCD的区别
1. GCD是纯C语言的API,NSOperationQueue是基于GCD的OC版本封装
2. GCD只支持FIFO的队列,NSOperationQueue可以很方便地调整执行顺序、设置最大并发数量
3. NSOperationQueue可以轻松在Operation间设置依赖关系,而GCD需要写很多的代码才能实现
4. NSOperationQueue支持KVO,可以监测operation是否正在执行(isExecuted)、是否结束(isFinished),是否取消(isCanceld)
5. GCD的执行速度比NSOperationQueue快
6.使用选择:
任务之间不太互相依赖:GCD
任务之间有依赖\或者要监听任务的执行情况:NSOperationQueue
8.多线程安全解决方案
1. 只在主线程刷新访问UI
2. 如果要防止资源抢夺,得用synchronized进行加锁保护
互斥锁使用格式:@synchronized(锁对象) { // 需要锁定的代码 }注意:锁定1份代码只用1把锁,用多把锁是无效的。
互斥锁的优缺点
优点:能有效防止因多线程抢夺资源造成的数据安全问题
缺点:需要消耗大量的CPU资源。
互斥锁的使用前提:多条线程抢夺同一块资源
相关专业术语:线程同步,多条线程按顺序地执行任务,互斥锁,就是使用了线程同步技术。
3. 如果异步操作要保证线程安全等问题, 尽量使用GCD(有些函数默认就是安全的)
OC在定义属性时有nonatomic和atomic两种选择
atomic:原子属性,为setter方法加锁(默认就是atomic)
nonatomic:非原子属性,不会为setter方法加锁。
原子和非原子属性的选择
nonatomic和atomic对比
atomic:线程安全,需要消耗大量的资源
nonatomic:非线程安全,适合内存小的移动设备
iOS开发的建议
所有属性都声明为nonatomic
尽量避免多线程抢夺同一块资源
尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力
二、网络