多线程

主要内容:
1 GCD队列循环等待、多读单写、组任务
2 NSOpertaion优点
3 NSThread实现原理
4 常用锁的区别

GCD

同步串行

在viewDidLoad中有下面一段代码

 dispatch_sync(dispatch_get_main_queue(), ^{
        [self doSomething];
   });

很多人说这会造成死锁。其实更准确的说法是:主队列循环等待造成死锁。


多线程_第1张图片
主队列

主队列是一个串行队列,viewDidLoad提交到了主线程执还未执行完毕,此时再同步提交doSomething到主线程。viewDidLoad被阻塞等待doSomething返回,而doSomething是后提交的任务,必须等前一个任务viewDidLoad执行完毕后,才能执行。这就是队列循环等待造成的死锁。
在viewDidLoad中代码改为如下

_serialQueue = dispatch_queue_create("thread_name", DISPATCH_QUEUE_SERIAL);
dispatch_sync(_serialQueue, ^{
        [self doSomething];
 });

这样会不会造成,队列循环等待呢?答案是:不会。


多线程_第2张图片

这里需要注意:只要是同步方式提交,不管是提交到串行队列还是并发队列,都是在当前线程执行。
所以,如图所示不会造成死锁,并且doSomething会在主线程中执行。

同步提交到并发队列

NSLog(@"1");
dispatch_sync(globalQueue, ^{
        NSLog(@"2");
        dispatch_sync(globalQueue, ^{
            NSLog(@"3");
        });
         NSLog(@"4");
  });
  NSLog(@"5");

并发队列特点:提交到队列的block可以并发执行。 打印结果12345

异步提交到并发队列

 dispatch_async(dispatch_get_global_queue(0, 0), ^{
      NSLog(@"1");
      [self performSelector:@selector(printLog) withObject:nil afterDelay:0];
       NSLog(@"3");
 });
-(void)printLog
{
    NSLog(@"2");
}

结果是:1和3。RunLoop在主线程是自动创建的,在子线程中是没有创建的。performSelector:withObject:afterDelay: 方法关键是afterDelay相当于创建了一个Timer提交到了RunLoop。等待下一次RunLoop循环事件时执行。而子线程中RunLoop根本就没有创建。所以printLog也就不会执行。

[self performSelector:@selector(printLog) withObject:nil];
[self performSelectorOnMainThread:@selector(printLog) withObject:nil waitUntilDone:YES];

请注意上面2个方法就是一个普通方法调用。与带afterDelay的有本质区别。

dispatch_barrier_async()

提供了一种多读单写技术,读与读可以并发,读与写是互斥的,写和写之间是互斥的。


多线程_第3张图片
#import "DataCenter.h"

@interface  DataCenter()
{
    dispatch_queue_t  _concurrent_queue;
    //用户数据中心,可能有多个线程需要访问
    NSMutableDictionary *_dataCenterDict;
}

@end

@implementation DataCenter

-(id)init
{
    self = [super init];
    if(self){
        _concurrent_queue = dispatch_queue_create("read_write_queue", DISPATCH_QUEUE_CONCURRENT);
        _dataCenterDict = [NSMutableDictionary dictionary];
    }
    return self;
}

-(id)objectForKey:(NSString*)key
{
    __block id obj;
    //同步读取指定数据: 若果是线程A调用,那么子啊线程A中执行。 若果是线程B调用,那么在线程B中执行
    //由于是并发队列,可以同时满足多个线程同时调用。
    dispatch_sync(_concurrent_queue, ^{
        obj = [_dataCenterDict objectForKey:key];
    });
    return obj;
}

-(void)setObject:(id)obj forKey:(NSString*)key
{
    //异步调用栅栏设置数据
    dispatch_barrier_async(_concurrent_queue, ^{
        [_dataCenterDict setObject:obj forKey:key];
    });
}

NSOperation

优点
1 添加依赖任务
2 任务执行状态控制
3 控制最大并发量

任务执行状态控制
isReady : 就绪
isExecuting: 执行中
isFinished: 执行完成
isCancelled: 已取消
只重写main()方法,系统控制任务状态,以及退出
重写了start()方法,需要自己控制任务状态
系统是怎样移除一个isFinished=YES的NSOperation的?KVO

NSThread

实现原理:内部创建了一个pthread线程,当执行完main函数后,系统会自动退出。


多线程_第4张图片

@synchronized

一般在创建单例对象的时候使用,保证在多线程环境下创建对象唯一

atomic

属性关键字,对被修饰对象进行原子操作(不负责使用)

@property(atomic)NSMutableArray *array;
self.array = [NSMutableArray array]; //保证线程安全
[self.array addObject: obj]; //不保证线程安全
OSSpinLock

循环等待访问,不释放当前资源, 专门用于轻量级访问,如+1,-1操作。如:引用计数

NSRecursiveLock

递归锁可以重入

NSLock

互斥锁,不可重入,上锁解锁,成对出现。

dispatch_semaphore_t
dispatch_semaphore_create(1);
dispatch_semaphore_wait(semphore,DISPATCH_TIME_FOREVER);
dispatch_semaphore_signal(semaphore)
dispatch_semaphore_create()
struct semaphore {
    int value;
    List //相关线程
}

dispatch_semaphore_wait 检测到S.value < 0 ,主动阻塞自己

dispatch_semaphore_wait()
{
    S.value = S.value - 1;
    if S.value < 0  then Block(S.List);  
}

dispatch_semaphore_signal 检查后,去唤醒线程。对于线程来说,是一个被动唤醒。

dispatch_semaphore_signal()
{
    S.value = S.value + 1;
    if(S.value <= 0) then wakeup(S.List)
}

总结

1 怎样GCD实现多读单写?
2 iOS提供了几种多线程技术,各自特点是什么?
3 NSOpertaion对象在Finished之后是怎样从queue当中移除的?
4 你多用过那些锁?结合实际谈谈你是怎样实现的?

你可能感兴趣的:(多线程)