事实上,多线程并不是多条线程同时执行的。在处理线程的过程中,由时间片控制cpu执行哪条线程,不过这过程太快了,会有种线程同时进行的感觉。设置线程的优先级可以享受到更多的cpu资源。
一.线程的优缺点
优点:
1.能适当提高程序的执行效率
2.能适当提高资源的利用率(cpu,内存)
缺点:
1.开启线程需要占用一定的内存空间,512kb
2.使程序更加复杂,需要控制好线程间的通信和数据共享问题。
UI更新为什么要在主线程
首先,这其实是苹果设计的一个规定,而非必须在主线程更新UI。
UIKit并不是一个线程安全的类,UI操作设计到渲染访问各种View对象的属性,异步操作下,会存在读写问题,如果为其加锁则会耗费大量资源并拖慢运行速度。所以干脆把他设计为只能在主线程更新的串行事件就行了。
二.异步渲染
提到UI渲染,那不得不提下大名鼎鼎的Texture,原名AsyncDisplayKit。
其实做iOS开发多年,我也一直在等哪个时机需要在实际项目中必须用到它,但是到现在为止,还没有。
https://www.jianshu.com/p/1186a99dded5
三.线程的生命周期
四.线程和Runloop的关系
1:runloop与线程是一一对应的,一个runloop对应一个核心的线程,为什么说是核心的,是因为runloop是可以嵌套的,但是核心的只能有一个,他们的关系保存在一个全局的字典里。
2:runloop是来管理线程的,当线程的runloop被开启后,线程会在执行完任务后进入休眠状态,有了任务就会被唤醒去执行任务。
3:runloop在第一次获取时被创建,在线程结束时被销毁。
4:对于主线程来说,runloop在程序一启动就默认创建好了。
5:对于子线程来说,runloop是懒加载的,只有当我们使用的时候才会创建,所以在子线程用定时器要注意:确保子线程的runloop被创建,不然定时器不会回调。
五.几种常用锁
上图为各种锁的性能排行。
其中,性能最好的OSSpinLock已经线程不再安全了。不推荐使用。
推荐使用:
1.dispatch_semaphore
2.NSLock
3.@synchronized
六.atomic与nonatomic
nonatomic:非原子属性,线程不安全。
atomic:原子属性,线程安全,自带一把自旋锁,但是耗资源。
七.自旋锁、互斥锁、读写锁
互斥锁
在访问共享资源之前对进行加锁操作,在访问完成之后进行解锁操作。 加锁后,任何其他试图再次加锁的线程会被阻塞,直到当前线程解锁。 如果解锁时有一个以上的线程阻塞,那么所有该锁上的线程都被编程就绪状态, 第一个变为就绪状态的线程又执行加锁操作,那么其他的线程又会进入等待。 在这种方式下,只有一个线程能够访问被互斥锁保护的资源。
自旋锁
自旋锁是一种特殊的互斥锁,当资源被枷锁后,其他线程想要再次加锁,此时该线程不会被阻塞睡眠而是陷入循环等待状态(CPU不能做其它事情),循环检查资源持有者是否已经释放了资源,这样做的好处是减少了线程从睡眠到唤醒的资源消耗,但会一直占用CPU的资源。适用于资源的锁被持有的时间短,而又不希望在线程的唤醒上花费太多资源的情况。
相对于互斥锁,自旋锁资源占用更厉害,但是唤醒时间短。
读写锁
只有一个线程可以占有写状态的锁,但可以有多个线程同时占有读状态锁。
八.GCD
GCD是Apple为多核的并行运算提出的解决方案
GCD会自动利用更多的CPU内核(双核,四核)
1.GCD的优缺点
优点
GCD会自动管理线程的生命周期(创建线程,调度任务,销毁线程),我们只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码。
缺点
GCD会自动管理线程的声明周期。在更复杂的线程操作中,无法自如的操作生命周期。
2.死锁
所谓死锁,通常指有两个线程A和B都卡住了,并等待对方完成某些操作。A不能完成是因为它在等待B完成。但B也不能完成,因为它在等待A完成。于是大家都完不成,就导致了死锁。
https://blog.csdn.net/abc649395594/article/details/48017245
3.栅栏
保证顺序执行
dispatch_barrier_sync
当所做事件有依赖关系时,可使用dispatch_barrier_sync。
如完成两张图片的下载后进行渲染。
- (void)demo{
// 栅栏函数
dispatch_queue_t concurrentQueue = dispatch_queue_create("testLabel", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQueue, ^{
NSString *logoStr = @"https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=3351002169,4211425181&fm=27&gp=0.jpg";
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:logoStr]];
UIImage *image = [UIImage imageWithData:data];
[self.mArray addObject:image];
});
dispatch_async(concurrentQueue, ^{
NSString *logoStr = @"https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3033952616,135704646&fm=27&gp=0.jpg";
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:logoStr]];
UIImage *image = [UIImage imageWithData:data];
[self.mArray addObject:image];
});
//加载完毕了 栅栏函数上
__block UIImage *newImage = nil;
dispatch_barrier_async(concurrentQueue, ^{
for (int i = 0; i<self.mArray.count; i++) {
UIImage *waterImage = self.mArray[I];
newImage =[KC_ImageTool kc_WaterImageWithWaterImage:waterImage backImage:newImage waterImageRect:CGRectMake(20, 100*(i+1), 100, 40)];
}
});
dispatch_async(concurrentQueue, ^{
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = newImage;
});
});
}
!!!!!!栅栏只能使用自定义并发队列,如果使用dispatch_get_global_queue会产生调用异常。
dispatch_get_global_queue是全局并发队列,如果加了barrier,那全局队列会产生堵塞,对于系统来说,是不允许的。
保证线程安全
for (int i = 0; i<2000; i++) {
dispatch_async(concurrentQueue, ^{
NSString *imageName = [NSString stringWithFormat:@"%d.jpg", (i % 10)];
NSURL *url = [[NSBundle mainBundle] URLForResource:imageName withExtension:nil];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
//!这里不加barrier会程序会崩溃
dispatch_barrier_async(concurrentQueue, ^{
[self.mArray addObject:image];
});
});
}
4.调度组
dispatch_group_t
相对于栅栏,group对于单个相同线程的需求降低了。
- (void)groupDemo{
//创建调度组
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_queue_t queue1 = dispatch_queue_create("com.lg.cn", DISPATCH_QUEUE_CONCURRENT);
// SIGNAL
dispatch_group_async(group, queue, ^{
NSString *logoStr = @"https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=3351002169,4211425181&fm=27&gp=0.jpg";
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:logoStr]];
UIImage *image = [UIImage imageWithData:data];
[self.mArray addObject:image];
});
dispatch_group_async(group, queue1, ^{
// afn --- sdk : queue: com.afn.cn
NSString *logoStr = @"https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3033952616,135704646&fm=27&gp=0.jpg";
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:logoStr]];
UIImage *image = [UIImage imageWithData:data];
[self.mArray addObject:image];
});
__block UIImage *newImage = nil;
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"数组个数:%ld",self.mArray.count);
for (int i = 0; i<self.mArray.count; i++) {
UIImage *waterImage = self.mArray[I];
newImage = [KC_ImageTool kc_WaterImageWithWaterImage:waterImage backImage:newImage waterImageRect:CGRectMake(20, 100*(i+1), 100, 40)];
}
self.imageView.image = newImage;
});
}
5.信号量
使用semaphore控制并发数。
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 信号量 -- gcd控制并发数
// 同步
//总结:由于设定的信号值为3,先执行三个线程,等执行完一个,才会继续执行下一个,保证同一时间执行的线程数不超过3
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
//任务1
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"执行任务1");
sleep(1);
NSLog(@"任务1完成");
dispatch_semaphore_signal(semaphore);
});
//任务2
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"执行任务2");
sleep(1);
NSLog(@"任务2完成");
dispatch_semaphore_signal(semaphore);
});
//任务3
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"执行任务3");
sleep(1);
NSLog(@"任务3完成");
dispatch_semaphore_signal(semaphore);
});
6.Source
- 其 CPU 负荷非常小,尽量不占用资源
- 联结的优势
在任一线程上调用它的的一个函数 dispatch_source_merge_data 后,会执行 Dispatch Source 事先定义好的句柄(可以把句柄简单理解为一个 block )
这个过程叫 Custom event ,用户事件。是 dispatch source 支持处理的一种事件
句柄是一种指向指针的指针 它指向的就是一个类或者结构,它和系统有很密切的关系
HINSTANCE(实例句柄),HBITMAP(位图句柄),HDC(设备表述句柄),HICON(图标句柄)等。这当中还有一个通用的句柄,就是HANDLE。
常用的dispatch_after也是使用Source实现的。
Source创建Timer
- (void)dispatchTimeWithPeriod:(double)period andCallback:(void (^)(dispatch_source_t timer))callback {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_source_t timer =dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,0, 0, queue);
dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), (uint64_t)(period *NSEC_PER_SEC), 0);
// 设置回调
dispatch_source_set_event_handler(timer, ^{
dispatch_async(dispatch_get_main_queue(), ^{
//编译时block会对timer对象强引用,使timer不会被过早释放
if (callback) callback(timer);
});
});
// 启动定时器
dispatch_resume(timer);
}
由于GCD和Runloop时平级的存在,所以以dispatch_source创造的Timer不会受到RunloopMode的影响。