多线程
摘自多线程
1.GCD并发本质实现机制
利用时间片轮转
2.如何实现线性编程
信号量 栅栏 调度组
3.NSLock
NSLock 遵循NSLocking协议 lock方法是加锁 unlock是解锁 trylock是尝试加锁 如果失败的话返回NO lockBeforeDate是在指定Date之前尝试加锁 如果在指定时间之前都不能加锁 则返回NO
@协议 NSLocking
-(无效)锁;
-(无效)解锁;
@结束
@interface NSLock:NSObject < NSLocking > {
@private
void * _priv;
}
-(BOOL)tryLock ;
-(BOOL)lockBeforeDate :(NSDate *)限制;
@property(nullable,copy)NSString *名称NS_AVAILABLE(10_5,2_0);
@结束
4.NSContion
@interface NSCondition:NSObject < NSLocking > {
@private
void * _priv;
}
-(无效)等待;
-(BOOL)waitUntilDate :(NSDate *)限制;
-(无效)信号;
-(无效)广播;
@property(nullable,copy)NSString *名称NS_AVAILABLE(10_5,2_0);
@结束
NSCondition的对象实际上作为一个锁和一个线程检查器,锁上之后其他线程也能上锁,而之后可以根据条件决定是否继续运行线程,即线程是否要进入waiting状态,经测试,直接进入waiting状态,当其他中线程的该锁执行signal或者broadcast方法时,线程被唤醒,继续运行之后的方法
5.条件锁- NSContionLock
@interface NSConditionLock:NSObject < NSLocking > {
@private
void * _priv;
}
-(instancetype)initWithCondition :(NSInteger)条件NS_DESIGNATED_INITIALIZER;
@property(readonly)NSInteger条件;
-(void)lockWhenCondition :(NSInteger)条件;
-(BOOL)tryLock ;
-(BOOL)tryLockWhenCondition :(NSInteger)条件;
-(无效)unlockWithCondition :(NSInteger)条件;
-(BOOL)lockBeforeDate :(NSDate *)限制;
-(BOOL)lockWhenCondition :(NSInteger)条件 beforeDate :(NSDate *)限制;
@property(nullable,copy)NSString *名称NS_AVAILABLE(10_5,2_0);
@结束
NSConditionLock状语从句:NSLock类似,遵循都NSLocking协议,方法都类似,多只是一个了condition属性,每个以及操作都多了一个关于condition属性的方法,例如tryLock,tryLockWhenCondition:,NSConditionLock可以称为条件锁,只有condition参数与初始化时候的condition相等,lock才能正确进行加锁操作。而unlockWithCondition:并非当Condition符合条件时才解锁,否则解锁之后,修改Condition的值
6.递归锁- NSRecursiveLock
@interface NSRecursiveLock:NSObject < NSLocking > {
@private
void * _priv;
}
-(BOOL)tryLock ;
-(BOOL)lockBeforeDate :(NSDate *)限制;
@property(nullable,copy)NSString *名称NS_AVAILABLE(10_5,2_0);
@end
NSRecursiveLock是递归归锁,他和NSLock的区别在于,NSRecursiveLock可以在一个线程中重复加锁(反正单线程内部任务是按顺序执行的,不会出现资源竞争问题),NSRecursiveLock会记录上锁和解锁的次数,当两者平衡的时候,才会释放锁,其他线程才可以上锁成功。
7.同步锁- Synchronized(self) {// code}
@synchronized(object)指令使用的object为该锁的唯一标识,只有当标识相同时,才满足互斥。
@synchronized(object) 简化了加锁的行为,我们不在需要显示的加锁
8.信号量- dispatch_semaphore
如果获取不到`锁`,重置当前线程中断,暂停,直到其他线程释放`锁`时,会唤醒当前线程。
9.自旋锁- OSSpinLock
自旋锁是一种“忙等”的锁,它适用于轻量访问,例如如在引用计数表和原子性atomic
如果当前线程的`锁`被其他线程获取,当前线程会不断探测`锁`是否有被释放,如果检测出释放,会第一时间获取这个锁
10.互斥锁- 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递归锁,允许同一个线程对同一个锁,并通过多次解锁解锁。
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 * __限制)
静态 pthread_mutex_t theLock;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&theLock,&attr);
11.分步锁- NSDistributedLock
在引用计数表的数据结构里,一张sideTable表利用分离锁被分成了多个部分。
这样可以对一张表的多个部分,同时进行操作,提升了效率
12.NSMutableArray,和NSMutableDictionary是线程安全的吗?NSCache呢
在做缓存时,优先使用NSCache而不是NSDictionary,我们熟悉的框架SDWebimage就是采用的NSCache。
NSCache 优点如下:
系统资源将要减少时,它可以自动减少减去缓存。
可以设置最大缓存数量。
可以设置最大占用内存值。
NSCache 线程是安全的
13.多线程的并行和并发有什么区别?
并行:充分利用计算机的多核,在多个线程上同步进行
并发:在一条线程上通过快速切换,让人感觉在同步进行
14.多线程有哪些优缺点?
创建线程是需要花费资源的
1.一条主线程占用1M,一条子线程占用512Kb。
2.线程的切换也是需要花费资源的
3.优点就是提升效率,充分利用了计算机的多核特性
15.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
16.解释一下多线程中的死锁?
死锁是由于多个线程(进程)在执行过程中,因为争夺资源而造成的互相等待现象,你可以理解为卡主了。产生死锁的必要条件有四个:
互斥条件 : 指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。
请求和保持条件 : 指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。
不可剥夺条件 : 指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。
环路等待条件 : 指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2");
});
NSLog(@"1");
// 什么也不会打印,直接报错
最常见的就是 同步函数 + 主队列 的组合,本质是队列阻塞。