概念篇
进程
正在进行的程序
线程
1. 一个进程至少包括一个线程,线程就是程序的执行流。程序执行任务的最小调度单位。
2. 程序启动时,会自动创建主线程。
3. 更新 UI 在主线程中执行
多线程
一个程序中有多条执行路径。在 iOS 中一些比较耗时的操作放在另外一条执行路径里,让它与主线程同时运行,这样不会造成主线程的阻塞。
单核多线程 & 多核多线程
1. 单核多线程:比单一主线程执行时间多,但是能防止卡顿。在多个线程之间进行切换
2. 多核多线程:多个 CPU 同时处理多个线程
并行 & 并发
1. 并发:在单核 CPU 以分时的方式,进行上下文切换,达到任务同时运行的效果
2. 并行:在多核 CPU 上真正意义上的多任务同时运行
同步 & 异步
1. 同步:优先级高,在线程中有执行顺序,不会开启新的线程
2. 异步:优先级低,在线程中没有执行顺序,开启新的线程,开启几条线程由队列决定。
队列
1. 管理任务在哪些线程中执行,FIFO(先进先出)
2. 主队列是串行队列
队列 & 任务 的执行方式
1. 同步不具备开启线程的能力,无论什么队列都不会开启线程
2. 异步函数具备开启线程的能力,开启几条线程由队列决定
Q:主队列中开启同步任务为什么会阻塞线程?
在主队列中开启同步任务,因为主队列是串行队列,里面的线程是有顺序的,先执行完一个线程才执行下一个线程,而主队列始终只有一个主线程,主线程是不会执行完毕的,因为他是无线循环的,除非关闭应用程序,因此在主线程开启一个同步任务,同步任务会抢占执行的资源,而主线程任务一直执行某些操作,不肯放手。两个的优先级都很高,最终导致死锁,阻塞线程。
NSLog(@"执行任务1")
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
NSLog(@"执行任务2")
});
NSLog(@"执行任务3")
// 主队列正在进行任务
// 执行任务 1
// 主队列加入同步任务
// 主队列会等待主线程执行完再执行同步任务
// 但是主线程执行完毕需要等待同步任务执行完毕
// 造成死锁
线程安全篇
多个线程可能会访问同一块资源,很容易引发 数据错乱 和 数据安全 问题。
解决方法
- 使用互斥锁,当某个线程调用次方法时,会获得该实例的对象锁,方法未结束之前,其他线程只能去等待,当方法执行完时,才会释放对象锁。
@synchronized(锁对象){
// 需要锁定的代码
} // 锁一份代码只用一把锁
- 使用 ReentrantLock ,在并发量比较高的情况下,建议使用;其他线程在等待指定时间内依然无法获取锁,那么会自动放弃该锁。
尽量将加锁、资源抢夺的业务逻辑交给服务端处理,减小移动端的压力。
优缺点
- 优点:使用线程同步技术,多条线程 按顺序地执行任务,能够有效防止多线程抢夺资源造成的数据安全问题。
- 缺点:需要消耗大量的 CPU 资源
原子性 和 非原子性
nonatomic 和 atomic 两种选择
- atomic:原子属性,为 setter 方法加锁,默认就是 atomic,需要消耗大量资源
- nonatomic:非原子属性,不会为 setter 方法加锁,非线程安全,适合内存小的移动设备
Q:使用 atomic 一定是线程安全吗?
atomic 只是在 setter 方法加了锁,防止多线程一直去写 @property; 假设 ABC 在写 @property,D 在读取,D 会读到一些随机值 (ABC 修改的值),这就不是线程安全了。
线程通讯
NSOject
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;// 主线程通讯
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait; // 指定线程通讯
延时执行 - NSObject
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
GCD
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 2秒后异步执行这里的代码...
});
//2.计算任务执行的时间
dispatch_time_t when=dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC));
//3.会在when这个时间点,执行queue中的这个任务
dispatch_after(when, queue1, ^{
NSLog(@"并发队列-延迟执行------%@",[NSThread currentThread]);
});
线程使用篇
NSThread
API
- 获取当前线程
[NSThread currentThread]
- 判断是否为主线程
[thread isMainThead]
- 线程暂停一段时间(线程阻塞,线程被移除可调度线程池,此时不可调度)
[NSThread sleepForTimeInterval:200];
// [NSThread sleepUntilDate:date];
- 退出线程(线程死亡(任务结束、发生异常、强制退出),从内存中移除)
[NSThread exit];
实现多线程方法
- 创建一个 NSThread 对象
(1)[NSThread detachNewThreadSelector:@select(thread1:) toTarget:self withObject: @"类方法实现多线程"]
(2)[self performSelectorInBackground:@selector(run) withObject:nil];
(3) NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(thread:) object:IMAGE_BEAUTY];
// 前两种方法线程自动执行,简单快捷,但是无法对线程进行更详细的设置,第三种方法需要 start 手动开启,可以在 start 前添加一些设置,比如优先级
- 添加 NSThread 标识
thread.name = @"美女";
- 设置线程执行的优先级(提高优先级执行概率)从 0-1(默认0.5)
// cpu在多线程之间切换, 并不能决定哪个线程先执行
thread.threadPriority = 0.9;
- 开始执行子线程
[thread start];
- 子线程执行代码
- (void)thread:(id)object {
// 开辟子线程的时候就需要添加自动释放池@autoreleasepool {
// 设置下载参数
NSURL *url = [[NSURL alloc]initWithString:object];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data]; // 回到主线程更新 UI
[self performSelectorOnMainThread:@selector(showImageOnMainThread:) withObject:image waitUntilDone:YES];
}
}
GCD
Grand Central Dispath 是 Apple 开发的一个多核编程的解决方法,基于 C 的 API.
GCD 是 libdispath 的市场名称,而 libdispath 作为 Apple 的一个库,为并发代码在多核硬件上提供有力支持。
GCD 的优势
1. GCD 是苹果公司为多核的并行运算提出的解决方案。
2. GCD 会自动利用更多的内核(如:双核 和 四核)
3. GCD 会自动管理线程的声明周期(创建线程、调度任务、销毁线程)
4. 程序员只需要告诉 GCD 想要执行的任务,不需要编写任何线程管理代码
GCD 执行过程
定制执行的任务,然后添加到对应的操作队列, GCD 将会自动将队列中的任务取出,放到对应的线程中执行。
- 同步方式
dispath_sync(dispath_queue_t queue, dispath_block_t block)// queue 队列,block 任务
- 异步方式
dispath_async(dispath_queue_t queue, dispath_block_t block)// queue 队列,block 任务
队列的创建
- 串行队列
dispath_queue_t dispath_queue_creat(const char *label, dispath_queue_attr_t attr);// label 队列名称,attr 队列属性,一般用 NULL
// 非 ARC 下需要手动释放手动创建的队列
dispath_release(queue);
凡是函数中带有 creat/copy/new/retain ,都需要在不需要使用这个数据的时候进行 release
GCD 的数据类型在 ARC 的环境下不需要再做 release
CF (core Foundation) 的数据类型在 ARC 环境下还是需要 release
获取主队列(串行队列)
dispath_get_main_queue()
- 并行队列
GCD 默认提供了全局的并发队列,无需手动创建
dispath_get_global_queue(dispath_queue_priority_t priority, unsigned long flags); // priority 设置优先级,flag 参数暂时不用,传 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 // 后台
- 自定义队列
dispatch_queue_t urls_queue = dispatch_queue_create("bloh.devtang.com",NULL);
dispatch_async(urls_queue, ^{
// your code
});
dispatch_release(urls_queue);
- 队列组(多个线程并行执行、然后等线程都结束后,再执行结果)
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
dispatch_group_async(group, dispatch_get_global_queue(0,0),^{
// 并行执行的线程一
dispatch_group_leave(group);// 异步返回标识
});
dispatch_group_async(group, dispatch_get_global_queue(0,0),^{
// 并行执行的线程二
});
dispatch_group_notify(group, dispatch_get_global_queue(0,0),^{
// 汇总结果
// dispatch_group_enter dispatch_group_leave 如果没有添加这一对的话,异步还没返回就会调 notify
});
常见应用
- 后台执行
dispatch_async(dispatch_get_global_queue(0,0),^{
// something
});
- 主线程执行
dispatch_async(dispatch_get_main_queue(),^{
// something
});
- 一次性执行
static dispatch_once_t onceToken;
dispatch_once(&onceToken,^{
// code to be executed once
});
- 延迟两秒执行
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispath_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime,dispatch_get_main_main_queue(),^(void){
// code to be executed on the main queue after delay
});
- 信号量 - 顺序执行
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(quene, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_semaphore_signal(semaphore);
});
dispatch_async(quene, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_semaphore_signal(semaphore);
});
dispatch_async(quene, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_semaphore_signal(semaphore);
});
网友评论