iOS多线程中,队列和执行的排列组合结果分析

本文是对以往学习的多线程中知识点的一个整理。

多线程中的队列有:串行队列,并发队列,全局队列,主队列

执行的方法有:同步执行和异步执行。那么两两一组合会有哪些注意事项呢?

如果不是在董铂然博客园看到这边文章请 点击查看原文

提到多线程,也就是四种,pthread,NSthread,GCD,NSOperation  

其中phtread是跨平台的。GCD和NSOperation都是常用的,后者是基于前者的。

但是两者区别:GCD的核心概念是将一个任务添加到队列,指定任务执行的方法,然后执行。 NSOperation则是直接将一个操作添加到队列中。

为了整体结构更加清晰,我是用GCD来做此排列组合的实验。实验主要是通过循环内打印和主线程的打印先后顺序来判断结果,最后再加以总结

1.串行队列,同步执行

    dispatch_queue_t q = dispatch_queue_create("dantesx", NULL);
    
    // 执行任务
    for (int i = 0; i<10; i++) {
        dispatch_sync(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    
    NSLog(@"董铂然 come here");

运行效果:

技术分享 

执行结果可以清楚的看到全在主线程执行,并且是按照数序执行,循环结束之后主线程的打印才输出。

2.串行队列,异步执行

    dispatch_queue_t q = dispatch_queue_create("dantesx", NULL);
    
    for (int i = 0; i<10; i++) {
        dispatch_async(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    //    [NSThread sleepForTimeInterval:0.001];
    NSLog(@"董铂然 come here");

 运行结果

技术分享

结果显示,系统开了1条异步线程,因此全部在线程2执行,并且是顺序执行。主线程打印虽然在最上面,但是这个先后顺序是不确定,如果睡个0.001秒,主线程的打印会混在中间。

3.并发队列,异步执行

    // 1. 队列
    dispatch_queue_t q = dispatch_queue_create("dantesx", DISPATCH_QUEUE_CONCURRENT);
    
    // 2. 异步执行
    for (int i = 0; i<10; i++) {
        dispatch_async(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    //    [NSThread sleepForTimeInterval:2.0];
    NSLog(@"董铂然 come here");

 运行结果

技术分享

结果显示,主线程的打印还是混在中间不确定的,因为异步线程就是谁也不等谁。系统开了多条线程,并且执行的顺序也是乱序的

 

4.并发队列,同步执行

    // 1. 队列
    dispatch_queue_t q = dispatch_queue_create("dantesx", DISPATCH_QUEUE_CONCURRENT);
    
    // 2. 同步执行
    for (int i = 0; i<10; i++) {
        dispatch_sync(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    //    [NSThread sleepForTimeInterval:2.0];
    NSLog(@"董铂然 come here");

 运行结果

技术分享

这个运行结果和第1种的串行队列,同步执行是一模一样的。 因为同步任务的概念就是按顺序执行,后面都要等。言外之意就是不允许多开线程。 同步和异步则是决定开一条还是开多条。

所以一旦是同步执行,前面什么队列已经没区别了

5.主队列,异步执行

    // 1. 主队列 - 程序启动之后已经存在主线程,主队列同样存在
    dispatch_queue_t q = dispatch_get_main_queue();
    // 2. 安排一个任务
    for (int i = 0; i<10; i++) {
        dispatch_async(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    NSLog(@"睡会");
    [NSThread sleepForTimeInterval:2.0];
    NSLog(@"董铂然 come here");

 运行结果

技术分享

结果显示有点出人意料。主线程在睡会之后才打印,循环一直在等着。因为主队列的任务虽然会加到主线程中执行,但是如果主线程里也有任务就必须等主线程任务执行完才轮到主队列的。

6.主队列,同步执行

    dispatch_queue_t q = dispatch_get_main_queue();
    
    NSLog(@"卡死了吗?");
    
    dispatch_sync(q, ^{
        NSLog(@"我来了");
    });
    
    NSLog(@"董铂然 come here");

 运行结果为卡死

卡死的原因是循环等待,主队列的东西要等主线程执行完,而因为是同步执行不能开线程,所以下面的任务要等上面的任务执行完,所以卡死。这是排列组合中唯一一个会卡死的组合。

7.同步任务的使用场景

    dispatch_queue_t q = dispatch_queue_create("dantesx", DISPATCH_QUEUE_CONCURRENT);
    // 1. 用户登录,必须要第一个执行
    dispatch_sync(q, ^{
        [NSThread sleepForTimeInterval:2.0];
        NSLog(@"用户登录 %@", [NSThread currentThread]);
    });
    // 2. 扣费
    dispatch_async(q, ^{
        NSLog(@"扣费 %@", [NSThread currentThread]);
    });
    // 3. 下载
    dispatch_async(q, ^{
        NSLog(@"下载 %@", [NSThread currentThread]);
    });
    NSLog(@"董铂然 come here");

 运行结果

技术分享

结果显示,“用户登陆”在主线程打印,后两个在异步线程打印。上面的“用户登陆”使用同步执行,后面的扣费和下载都是异步执行。所以“用户登陆”必须第一个打印出来不管等多久,然后后面的两个异步和主线程打印会不确定顺序的打印。这就是日常开发中,那些后面对其有依赖的必须要先执行的任务使用同步执行,然后反正都要执行先后顺序无所谓的使用异步执行。

8.block异步任务包裹同步任务

    dispatch_queue_t q = dispatch_queue_create("dantesx", DISPATCH_QUEUE_CONCURRENT);
    void (^task)() = ^ {
        // 1. 用户登录,必须要第一个执行
        dispatch_sync(q, ^{
            NSLog(@"用户登录 %@", [NSThread currentThread]);
        });
        // 2. 扣费
        dispatch_async(q, ^{
            NSLog(@"扣费 %@", [NSThread currentThread]);
        });
        // 3. 下载
        dispatch_async(q, ^{
            
            NSLog(@"下载 %@", [NSThread currentThread]);
        });
    };
    
    dispatch_async(q, task);
    [NSThread sleepForTimeInterval:1.0];
    NSLog(@"董铂然 come here");

 运行结果

技术分享

因为整个block是在异步执行的,所以即使里面“用户登陆”是同步执行,那也无法在主线程中执行,只能开一条异步线程执行,因为是同步的所以必须等他先执行,后面的“扣费”和“下载”在上面同步执行结束之后,不确定顺序的打印。

9.全局队列

    dispatch_queue_t q = dispatch_get_global_queue(0, 0);
    
    for (int i = 0; i < 10; i++) {
        dispatch_async(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    [NSThread sleepForTimeInterval:1.0];
    NSLog(@"com here");

 运行结果

技术分享

全局队列的本质就是并发队列,只是在后面加入了,“服务质量”,和“调度优先级” 两个参数,这两个参数一般为了系统间的适配,最好直接填0和0

如果不是在董铂然博客园看到这边文章请 点击查看原文

总结:

1. 开不开线程,取决于执行任务的函数,同步不开,异步开。

2. 开几条线程,取决于队列,串行开一条,并发开多条(异步)

3. 主队列:  专门用来在主线程上调度任务的"队列",主队列不能在其他线程中调度任务!

4. 如果主线程上当前正在有执行的任务,主队列暂时不会调度任务的执行!主队列同步任务,会造成死锁。原因是循环等待

5. 同步任务可以队列调度多个异步任务前,指定一个同步任务,让所有的异步任务,等待同步任务执行完成,这是依赖关系。

6. 全局队列:并发,能够调度多个线程,执行效率高,但是相对费电。 串行队列效率较低,省电省流量,或者是任务之间需要依赖也可以使用串行队列。

7. 也可以通过判断当前用户的网络环境来决定开的线程数。WIFI下6条,3G/4G下2~3条。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。