1.NSThread相关知识
2.GCD 相关知识?(栅栏函数、Group、定时器、信号量、队列类型、任务派发方式、快速迭代、延迟处理)
1.栅栏函数(控制任务的执行顺序)
dispatch_barrier_async(queue, ^{
NSLog(@"barrier");
});
2.延迟执行(延迟·控制在哪个线程执行)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"---%@",[NSThread currentThread]);
});
3.一次性代码
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"-----");
});
4.快速迭代(开多个线程并发完成迭代操作)
dispatch_apply(subpaths.count, queue, ^(size_t index) {
});
5.队列组(同栅栏函数)
dispatch_group_t group = dispatch_group_create();
// 队列组中的任务执行完毕之后,执行该函数
dispatch_group_notify(dispatch_group_t group,dispatch_queue_t queue,dispatch_block_t block);
// 进入群组和离开群组
dispatch_group_enter(group);//执行该函数后,后面异步执行的block会被gruop监听
dispatch_group_leave(group);//异步block中,所有的任务都执行完毕,最后离开群组
//注意:dispatch_group_enter|dispatch_group_leave必须成对使用
6.信号量(并发编程中很有用)
3.NSOperation 和 NSOperationQueue相关知识?
(最大并发数、线程依赖)
4.如何实现线性编程?
信号量、栅栏、Dispatch_group
5.说一下 GCD 并发队列实现机制?
利用的时间片轮转
6.NSLock
NSLock遵循NSLocking协议,lock方法是加锁,unlock是解锁,tryLock是尝试加锁,如果失败的话返回 NO,lockBeforeDate:是在指定Date之前尝试加锁,如果在指定时间之前都不能加锁,则返回NO。
@protocol NSLocking
- (void)lock;
- (void)unlock;
@end
@interface NSLock : NSObject <NSLocking> {
@private
void *_priv;
}
- (BOOL)tryLock;
- (BOOL)lockBeforeDate:(NSDate *)limit;
@property (nullable, copy) NSString *name NS_AVAILABLE(10_5, 2_0);
@end
7.NSContion
@interface NSCondition : NSObject <NSLocking> {
@private
void *_priv;
}
- (void)wait;
- (BOOL)waitUntilDate:(NSDate *)limit;
- (void)signal;
- (void)broadcast;
@property (nullable, copy) NSString *name NS_AVAILABLE(10_5, 2_0);
@end
NSCondition 的对象实际上作为一个锁和一个线程检查器,锁上之后其它线程也能上锁,而之后可以根据条件决定是否继续运行线程,即线程是否要进入 waiting 状态,经测试,直接进入 waiting 状态,当其它线程中的该锁执行 signal 或者 broadcast 方法时,线程被唤醒,继续运行之后的方法。
8.条件锁 - NSContionLock
@interface NSConditionLock : NSObject <NSLocking> {
@private
void *_priv;
}
- (instancetype)initWithCondition:(NSInteger)condition NS_DESIGNATED_INITIALIZER;
@property (readonly) NSInteger condition;
- (void)lockWhenCondition:(NSInteger)condition;
- (BOOL)tryLock;
- (BOOL)tryLockWhenCondition:(NSInteger)condition;
- (void)unlockWithCondition:(NSInteger)condition;
- (BOOL)lockBeforeDate:(NSDate *)limit;
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
@property (nullable, copy) NSString *name NS_AVAILABLE(10_5, 2_0);
@end
NSConditionLock 和 NSLock 类似,都遵循 NSLocking 协议,方法都类似,只是多了一个 condition 属性,以及每个操作都多了一个关于 condition 属性的方法,例如 tryLock,tryLockWhenCondition:,NSConditionLock 可以称为条件锁,只有 condition 参数与初始化时候的 condition 相等,lock 才能正确进行加锁操作。而 unlockWithCondition: 并不是当 Condition 符合条件时才解锁,而是解锁之后,修改 Condition 的值。
9.递归锁 - NSRecursiveLock
@interface NSRecursiveLock : NSObject <NSLocking> {
@private
void *_priv;
}
- (BOOL)tryLock;
- (BOOL)lockBeforeDate:(NSDate *)limit;
@property (nullable, copy) NSString *name NS_AVAILABLE(10_5, 2_0);
@end
NSRecursiveLock 是递归锁,他和 NSLock 的区别在于,NSRecursiveLock 可以在一个线程中重复加锁(反正单线程内任务是按顺序执行的,不会出现资源竞争问题),NSRecursiveLock 会记录上锁和解锁的次数,当二者平衡的时候,才会释放锁,其它线程才可以上锁成功。
10.同步锁 - Synchronized(self) {// code}
@synchronized(object) 指令使用的 object 为该锁的唯一标识,只有当标识相同时,才满足互斥。
@synchronized(object) 简化了加锁的行为,我们不在需要显示的加锁。
##11.信号量 - dispatch_semaphore。
如果获取不到
锁,会将当前线程阻塞、休眠,直到其他线程释放锁时,会唤醒当前线程。
12.自旋锁 - OSSpinLock
自旋锁是一种 “忙等” 的锁,它适用于轻量访问,譬如在
引用计数表和 原子性atomic。
如果当前线程的 锁 被其他线程获取,当前线程会不断探测 锁 是否有被释放,如果检测出释放,会第一时间获取这个锁
13.互斥锁 - pthread_mutex
// 初始化方法()
int pthread_mutex_init(pthread_mutex_t * __restrict, const pthread_mutexattr_t * __restrict);
// pthread_mutex_t * __restrict 代表互斥锁的类型,有以下四种
1.PTHREAD_MUTEX_NORMAL 缺省类型,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后先进先出原则获得锁。
2.PTHREAD_MUTEX_ERRORCHECK 检错锁,如果同一个线程请求同一个锁,则返回 EDEADLK,否则与普通锁类型动作相同。这样就保证当不允许多次加锁时不会出现嵌套情况下的死锁。
3.PTHREAD_MUTEX_RECURSIVE 递归锁,允许同一个线程对同一个锁成功获得多次,并通过多次 unlock 解锁。
4.PTHREAD_MUTEX_DEFAULT 适应锁,动作最简单的锁类型,仅等待解锁后重新竞争,没有等待队列。
// 常用的一些方法
int pthread_mutex_lock(pthread_mutex_t *);
int pthread_mutex_trylock(pthread_mutex_t *);
int pthread_mutex_unlock(pthread_mutex_t *);
int pthread_mutex_destroy(pthread_mutex_t *);
int pthread_mutex_setprioceiling(pthread_mutex_t * __restrict, int,int * __restrict);
int pthread_mutex_getprioceiling(const pthread_mutex_t * __restrict,int * __restrict);
如何利用 pthread_mutex 创建一个递归锁
static pthread_mutex_t theLock;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&theLock, &attr);
14.分步锁 - NSDistributedLock。
在 引用计数表的数据结构里,一张 sideTable 表利用 分离锁 被分成了多个部分。
这样可以对一张表的多个部分,同时进行操作,提升了效率。
15.如何确保线程安全?
- 采用
串行队列。 - 加上
同步锁。
16.NSMutableArray、和 NSMutableDictionary是线程安全的吗?NSCache呢?
在做缓存时,优先使用
NSCache而不是NSDictionary,我们熟悉的框架SDWebimage就是采用的NSCache。
NSCache 优点如下:
- 系统资源将要耗尽时,它可以自动删减缓存。
- 可以设置最大缓存数量。
- 可以设置最大占用内存值。
NSCache线程是安全的。
17.多线程的 并行 和 并发 有什么区别?
并行:充分利用计算机的多核,在多个线程上同步进行 并发:在一条线程上通过快速切换,让人感觉在同步进行
18.多线程有哪些优缺点?
创建线程是需要花费资源的
- 一条主线程占用1M,一条子线程占用 512Kb。
- 线程的切换也是需要花费资源的
- 优点就是提升效率,充分利用了计算机的多核特性。
19.如何自定义 NSOperation ?
需要重写其 main 方法
20.GCD 与 NSOperationQueue 有哪些异同?
- 1>
GCD是纯C语言的API。NSOperationQueue是基于GCD的OC的封装。 - 2>
GCD只支持FIFO队列,NSOperationQueue可以方便设置执行顺序,设置最大的并发数量。 - 3>
NSOperationQueue可是方便的设置operation之间的依赖关系,GCD则需要很多代码。 - 4>
NSOperationQueue支持KVO,可以检测operation是否正在执行(isExecuted),是否结束(isFinished),是否取消(isCanceled) - 5>GCD的执行速度比NSOperationQueue快。
使用场合:
-
任务之间不太相互依赖:
GCD -
任务之间有依赖或要监听任务的执行情况:
NSOperationQueue
21.解释一下多线程中的死锁?
死锁是由于多个线程(进程)在执行过程中,因为争夺资源而造成的互相等待现象,你可以理解为卡主了。产生死锁的必要条件有四个:
- 互斥条件 : 指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。
- 请求和保持条件 : 指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。
- 不可剥夺条件 : 指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。
- 环路等待条件 : 指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源。
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2");
});
NSLog(@"1");
// 什么也不会打印,直接报错
最常见的就是 同步函数 + 主队列 的组合,本质是队列阻塞。
22.分类和类拓展的区别?
-
1.
分类的加载在运行时,类拓展的加载在编译时。 -
2.
分类不能给系统的类添加方法。 -
3.
类拓展只以声明的形式存在,一般存在 .m 文件中。
持续更新中~