iOS 中GCD常见面试题

示例1

- (void)surpassNum

{
    while (self.num < 500) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            self.num++;
        });
    }
    NSLog(@"num is %d",self.num);
}
复制代码

结果:打印结果为>=500

分析:

  1. 当首次进入循环时,self.num = 0,此时创建一个异步任务,但是这个任务并不一定是立马执 行,会等待先出的调度,此时就会进入下一次循环,由于任务为执行,所以self.num++也就未 执行,所以第二次循环时,self.num还是等于0,此时又会创建一个新的异步任务,以此类推, 至少都会常见500条异步任务

  2. 由于while循环中使用了self.num<500作为终止条件,所以只有当self.num >=500 时才能跳出循环,执行打印

  3. 在跳出循环执行打印的中间时间,可能存在多个异步任务执行了,也就相当于self.num++被执行了N次

  4. 所以打印结果必须 >=500

扩展:如果把 dispatch_async 换成 dispatch_sync 同步任务,则打印结果必定为 500,因为同步任务会堵塞线程, self.num++ 没执行完毕之前,下一次循环就不可能执行。

示例2

- (void)fixNum
{
    for(int i= 0; i < 1000; i++){
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            self.num++;
        });
    }
    NSLog(@"fix num is %d", self.num);
}
复制代码

结果: 打印结果0-999 分析:

  1. 这里使用了for循环,并且使用了临时变量i,而不是self.num来进行判断,self.num和循环没有关系, 所以这里只会执行999次循环,也就是创建了999个异步任务

  2. 当i== 1000时for循环终止,此时虽然创建了999个异步任务,但是这些任务执行完毕了吗? 显然没有,需要等待线程来调度;所以在打印之前,到底有多少个任务能够被执行,这个不确定, 可能一个任务都没执行,可能都执行完毕,那么打印结果就有可能是0-999

  3. 虽然理论上是0-999,但是打印结果还是会趋近于999

示例3

- (void)asynDemo

{
    dispatch_queue_t queue = dispatch_queue_create("com.qihu", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        NSLog(@"1");
    });

    dispatch_async(queue, ^{
        NSLog(@"2");
    });

    dispatch_sync(queue, ^{
        NSLog(@"3");
    });

    NSLog(@"0");

    dispatch_async(queue, ^{
        NSLog(@"4");
    });

    dispatch_async(queue, ^{
        NSLog(@"5");
    });
    dispatch_async(queue, ^{
        NSLog(@"6");
    });
}
复制代码

问:上面的打印结果可能为什么?

  • A: 1230456
  • B: 1234560
  • C: 3120465
  • D: 2134560

答: A和C 分析

  1. 任务3为同步任务,同步任务有个特性就是会阻塞线程的执行,所以在任务3未执行完毕之前,任务0以及之后的任务都会处于阻塞状态,所以3必定在0之前打印
  2. 然后根据函数的按顺序执行的规律,任务0必定会在 4、5、6 之前进行打印
  3. 异步任务之间是无序的,所以 1、2、3 之间顺序不定,4、5、6 之间顺序也是不定

如果 queue 是 DISPATCH_QUEUE_SERIAL 类型的串行队列的话则答案是 A

示例4

- (void)barrierTest
{
    dispatch_queue_t queue = dispatch_queue_create("com.barrier", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        NSLog(@"1");
    });

    dispatch_async(queue, ^{
        NSLog(@"2");
    });

    dispatch_barrier_async(queue, ^{
        NSLog(@"3");
    });

    dispatch_async(queue, ^{
        NSLog(@"4");
    });

}
复制代码

结果:

1234 或 2134

分析:

  1. queue 是一个 DISPATCH_QUEUE_CONCURRENT 类型的并发队列,所以任务1和任务2顺序是不定的

 2. 任务3使用了 dispatch_barrier_async 栅栏函数来创建任务,它有个特点,就是会将前后任

 务进行隔离,任务1和任务2必须都执行完了才会执行任务3,任务4必须等任务3执行完了才能执行

 3. 所以任务3在任务1和任务2之后,但是在任务4之前

示例5

- (void)asynDemo1

{
    dispatch_queue_t queue = dispatch_queue_create("com.demo", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"1");
    dispatch_async(queue, ^{
        NSLog(@"2");
        dispatch_async(queue, ^{
            NSLog(@"3");
        });
        NSLog(@"4");
    });
    NSLog(@"5");
}
复制代码

结果:

15243

分析:

  1. 按执行顺序,先打印1

 2. dispatch_async创建了异步线程,在后台等待,继续往下执行打印5

 3. 异步任务被调用后,先打印2,然后创建一个新异步任务等待调度执行,继续往下执行打印4

 4. 最后打印3

示例6

- (void)syncdemo2 {
    dispatch_queue_t queue = dispatch_queue_create("com.demo", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"1");
    dispatch_async(queue, ^{
        NSLog(@"2");
        dispatch_sync(queue, ^{ NSLog(@"3"); });
        NSLog(@"4");
    });
    NSLog(@"5");
}
复制代码

结果:

15234

分析:

  1. 按顺序执行,先打印 1

 2. dispatch_async 创建了异步任务,在后台等待执行,继续往下执行打印 5

 3. 异步任务被调用后,先打印 2

 4. 创建一个同步任务堵塞线程,等待同步任务执行完毕后才能继续往下执行,所以打印 3

 5. 最后打印 4

示例7

- (void)deadLockTest
{
    dispatch_queue_t queue = dispatch_queue_create("com.demo", NULL);
    NSLog(@"1");
    dispatch_async(queue, ^{
        NSLog(@"2");
        dispatch_sync(queue, ^{
            NSLog(@"3");
        });
        NSLog(@"4");
    });
    NSLog(@"5");
}
复制代码

结果:

152崩溃

分析:

  1. 按顺序执行,先打印1

 2. dispatch_async创建了异步任务,在后台等待执行,继续往下执行打印5

 3. 异步任务被调用,先打印2

 4. 创建一个同步任务3,由于queue是串行队列,任务3需要等待异步任务执行完毕后才能执行,但是异步任务     又需要等待任务3执行完毕后才能继续往下执行打印4,你依赖我,我依赖你,这里就造成了死锁,最终导致     EXC_BAD_INSTRUCTION 异常错误

猜你喜欢

转载自juejin.im/post/7081582721415249950
今日推荐