多线程

作者: 一只长毛猫 | 来源:发表于2018-04-20 16:51 被阅读17次

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

    GCD

    同步串行

    在viewDidLoad中有下面一段代码

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

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


    主队列

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

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

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



    这里需要注意:只要是同步方式提交,不管是提交到串行队列还是并发队列,都是在当前线程执行。
    所以,如图所示不会造成死锁,并且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()

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


    #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函数后,系统会自动退出。


    @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<thread> //相关线程
    }
    

    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 你多用过那些锁?结合实际谈谈你是怎样实现的?

    相关文章

      网友评论

          本文标题:多线程

          本文链接:https://www.haomeiwen.com/subject/qetzkftx.html