美文网首页iOS 开发 Objective-C
第九篇:Objective-C 知识回顾多线程

第九篇:Objective-C 知识回顾多线程

作者: 望穿秋水小作坊 | 来源:发表于2019-11-20 17:15 被阅读0次
    多线程大纲

    9.1.同步/异步 和 串行/并发

    问题一:同步/异步 和 串行/并发组合有哪些?
    • dispatch_sync(serial_queue, ^{//任务});
    • dispatch_async(serial_queue, ^{//任务});
    • dispatch_sync(concurrent_queue, ^{//任务});
    • dispatch_async(concurrent_queue, ^{//任务});
    问题二:分析下面代码是否存在问题?
    - (void)viewDidLoad {
        [super viewDidLoad];
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"Hello world!");
        });
    }
    
    • 会引起死锁
    • 因为 Block 任务和 viewDidLoad 任务都要在主线程中执行,并且在同一个串行队列中,而这两个任务需要相互等待对方任务的完成才能继续进行,就造成了等待死锁。
    问题三:分析下面代码是否存在问题?
    - (void)viewDidLoad {
        [super viewDidLoad];
        dispatch_queue_t serialQueue = dispatch_queue_create("apple.searial", DISPATCH_QUEUE_SERIAL);
        dispatch_sync(serialQueue, ^{
            NSLog(@"Hello world!");
        });
    }
    
    • 不会引起死锁
    • Block 任务和 viewDidLoad 任务都要在主线程中执行,但是他们位于两个不同的串行队列中,而这两个任务不需要需要相互等待对方任务的完成就能继续进行,因此不会造成死锁。
    问题四:分析下面代码是输出是什么?
    - (void)viewDidLoad {
        [super viewDidLoad];
        NSLog(@"1");
        dispatch_sync(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"2");
            dispatch_sync(dispatch_get_global_queue(0, 0), ^{
                NSLog(@"3");
            });
            NSLog(@"4");
        });
        NSLog(@"5");
    }
    
    
    • 1 2 3 4 5
    • 问题的关键点在于并发队列,可以并发执行,即使是在同步的方式下进行,也不需要等待前面的任务,就不会造成死锁。
    • global_queue 如果换成自定义的串行队列,就产生了和问题一相同的死锁。
    问题五:下面代码熟悉吗?
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"这是我们开发中最常用的写法");
        });
    
    • 这是我们开发中最常用的写法
    • 一般用于在其他子线程执行玩任务后,需要回到主线程进行 UI 绘制工作的时候用。
    问题五:分析下面代码是输出是什么?
    - (void)viewDidLoad {
        [super viewDidLoad];
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"1");
            [self performSelector:@selector(printLog) withObject:nil afterDelay:0];
            NSLog(@"3");
        });
    }
    
    - (void)printLog {
        NSLog(@"2"); 
    }
    
    • 这里其实考察的是对 performSelector: withObject: afterDelay: 方法的理解。打印 1 3
    • performSelector: withObject: afterDelay: 方法必须在有 runloop 的线程中调用,它需要把任务加到 runloop 中去,而全局并发队列中没有 runloop,所以,会直接失效。

    9.2.dispatch_barrier_async()

    问题一:怎么利用 GCD 实现多读单写?
    流程分析
    @implementation GlobalUserInfo
    
    + (GlobalUserInfo*) shareUserInfo {
        static GlobalUserInfo *instance;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            instance = [[self alloc] init];
        });
        return instance;
    }
    
    - (instancetype)init
    {
        self = [super init];
        if (self) {
            concurrentQueue = dispatch_queue_create("userinfo_read_write", DISPATCH_QUEUE_CONCURRENT);
            userInfoDictionary = [[NSMutableDictionary alloc] init];
            // 假设对年龄这个数据的多读单写
            [userInfoDictionary setValue:@(1) forKey:@"age"];
        }
        return self;
    }
    
    - (int)getAge {
        __block id obj;
        // 同步读取指定数据
        dispatch_sync(concurrentQueue, ^{
            obj = [userInfoDictionary objectForKey:@"age"];
        });
        return [obj intValue];
    }
    - (void)setAge:(int)age {
        // 异步栅栏调用设置数据
        dispatch_barrier_async(concurrentQueue, ^{
            [self->userInfoDictionary setValue:@(age) forKey:@"age"];
            // 模拟耗时的写操作,才能看到效果
            [NSThread sleepForTimeInterval:2.0];
            NSLog(@"write queue4 %i", age);
        });
    }
    @end
    

    9.3.dispatch_group_async()

    问题一:怎么利用 GCD 实现这个需求:A、B、C 三个任务并发,完成后执行任务 D?
    - (void)learnGCDGroup {
        // 模拟真实开发中多个数据资源需要下载
        NSMutableArray *urlArray = [[NSMutableArray alloc] init];
        for (int i = 0 ; i < 10; i++) {
            [urlArray addObject:@(i)];
        }
        
        // 创建一个group
        dispatch_group_t group = dispatch_group_create();
        
        // 创建一个并发队列
        dispatch_queue_t concurrentQueue = dispatch_queue_create("download_concurrent_queue", DISPATCH_QUEUE_CONCURRENT);
        
        // 进行并发图片下载
        for (id obj in urlArray) {
            // 模拟耗时下载
            dispatch_group_async(group, concurrentQueue, ^{
                int randomTime = arc4random()% 3;
                [NSThread sleepForTimeInterval:randomTime];
                NSLog(@"download resource finished %@",obj);
            });
        }
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
            NSLog(@"所有资源已经下载完毕,可以再主线程更新 UI 了.");
        });
    }
    

    9.4.NSOperationQueue

    问题一:哪些特点的方法使用 NSOperation 更为方便呢?
    • 添加任务依赖
    • 任务执行状态
    • 最大并发量
    问题二:我们可以控制 NSOperation 的哪些状态?
    • isReady
    • isExecuting
    • isFinished
    • isCancelled
    问题三:从代码的角度,我们如何控制 NSoperation 的状态?
    • 如果只重写了 main 方法,底层控制变更任务执行完成状态,以及任务退出。
    • 如果重写了 start 方法,自行控制任务状态。
    问题四:系统是怎样移除一个 isFinished=YES 的 NSOperation 的?
    • 答案:通过 KVO

    9.6.多线程 和 锁

    问题一:在 iOS 当中有哪些锁呢?或者说你使用过哪些锁?
    • @synchoroinized(一般在创建单例对象的时候使用)
    • atomic(对被修饰对象进行原子操作,不负责使用
    • OSSpinLock(循环等待访问,不释放当前资源,用于轻量级数据访问,简单的 int 值+1/-1 操作)
    • NSRecursiveLock(适合在递归操作中使用)
    • NSLock
    • dispatch_semaphore_t

    相关文章

      网友评论

        本文标题:第九篇:Objective-C 知识回顾多线程

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