Look技术面试题收集

IOS面试题(多线程) --- GCD

2022-04-22  本文已影响0人  ShawnAlex

OC面试题目合集地址

问题1: GCD调用的四种组合/ 讲一下你对GCD的了解

dispatch_sync(serial_queue, ^{ 任务 })
dispatch_async(serial_queue, ^{ 任务 })
dispatch_sync(concurrent_queue, ^{ 任务 })
dispatch_async(concurrent_queue, ^{ 任务 })

同步函数不具备开启线程的能力,无论是什么队列都不会开启线程;异步函数具备开启线程的能力,开启几条线程由队列决定(串行队列只会开启一条新的线程,并发队列会开启多条线程)

同步函数 dispatch_sync

异步函数 dispatch_async

留意下, 异步有不新开线程情况

任务追加线程已存在,比如 main 线程,则不会开线程,而是使用线程。例如: 在串行队列Queue任务A里异步将任务B追加到队列Queue中,此时任务B任务A在一个线程,不开线程。


问题2: GCD自定义queue有几种类型

serial串行

又称为private dispatch queues,同一时刻只执行一个任务,并按添加到serial的顺序执行。当创建多个serial queue时,虽然它们各自是同步执行的,但serial queueserial queue之间是并发执行的。serial queue通常用于同步访问特定的资源或数据。

concurrent并行

又称为global dispatch queue,同一时刻可执行多个任务,任务开始执行的顺序按添加的顺序执行,但是执行完成的顺序是随机的,同时可以创建执行的任务数量依赖系统条件。

Main dispatch queue 主队列

全局可用的serial queue,它是在应用程序主线程上执行任务的。


问题3: 下面代码运行结果会什么样?

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    NSLog(@"1");
    
    dispatch_sync(dispatch_get_main_queue(), ^{
        
        NSLog(@"2");
        
    });
    
    NSLog(@"3");
}

考察同步串行问题

答案:

1 然后 Crash

其实因为这里的dispatch_sync是个死锁

    // 死锁
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"2");
    });

这段代码的进行的逻辑为


问题3逻辑

验证:

问题3结果

问题4: 下面代码运行结果会什么样?

    NSLog(@"1");

    dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL);

    dispatch_sync(queue, ^{

        NSLog(@"2");

    });

    NSLog(@"3");

跟问题3不同, 这端代码涉及2个队列, 主队列和自定义串行队列queue

问题4图解

所以正常运行

答案:

正常运行 1 2 3

问题4答案

当然如果创建的队列是主队列, 如下

    // dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue = dispatch_get_main_queue();

那么问题4例子还是跟问题3一样, 死锁 crash


问题5: 下面代码运行结果会什么样?

- (void)viewDidLoad {
    [super viewDidLoad];

    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    NSLog(@"1");
    
    dispatch_sync(queue, ^{
        
        NSLog(@"2");
        
        dispatch_sync(queue, ^{
            
            NSLog(@"3");
            
        });
        
        NSLog(@"4");
        
    });
    
    NSLog(@"5");
  
}

答案:

正常运行 1 2 3 4 5

问题5结果

问题6: 下面代码运行结果会什么样?

- (void)viewDidLoad {
    [super viewDidLoad];

    dispatch_queue_t queue = dispatch_queue_create("test1", DISPATCH_QUEUE_SERIAL);
    
    NSLog(@"1");

    dispatch_sync(queue, ^{

        NSLog(@"2");

        dispatch_sync(queue, ^{

            NSLog(@"3");

        });

        NSLog(@"4");

    });

    NSLog(@"5");

}

其实这个道题跟问题3 道理相同

原因都是: 由队列引起的循环等待造成 死锁

答案:

异常运行 1 2 Crash

问题6结果

问题7: 下面代码运行结果会什么样?

- (void)viewDidLoad {
    [super viewDidLoad];

    NSLog(@"1");

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

异步分配到串行队列, 这里是主队列。viewDidLoad执行结束之后, 执行异步任务dispatch_async

答案:

运行正常 1 2

问题7结果

问题8: 下面代码运行结果会什么样?

- (void)viewDidLoad {
    [super viewDidLoad];

    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
   
    dispatch_async(queue, ^{
        
        NSLog(@"1");
        
        [self performSelector:@selector(testAction1)];
        [self performSelector:@selector(testAction2) withObject:nil afterDelay:0];
         
        NSLog(@"4");
        
    });
    
    
}

- (void)testAction1 {
    
    NSLog(@"2");
    
}


- (void)testAction2 {
    
    NSLog(@"3");
    
}

NSObject方法 NSRunLoop方法

答案:

运行正常 1 2 4

问题8结果

问题9: 怎样用GCD实现多读单写

例子

例如内存中维护一个字典, 有多个读者和写者操作这块数据

所以我们设计的模式就是


设计模式

当做写处理的时候通过一个"栅栏"来挡住读处理, 等写处理完成之后再进行读处理

GCD 栅栏函数

dispatch_barrier_async(异步队列, ^{ // 写操作 } );

看个例子, 有三组并发队列, 队列A读取, 队列B更改数据, 队列C读取更改完的数据

如果我们不做任何操作, 如下

@interface ViewController ()

@property(nonatomic, assign) int a;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
 
    dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);

    
    dispatch_async(queue, ^{
        NSLog(@"队列A读取: %d", self.a);
    });
    
    __weak typeof(self) weakSelf = self;
    
    dispatch_async(queue, ^{
        
        for (int i = 0; i < 10; i++) {
            weakSelf.a = weakSelf.a + 1;
            
            NSLog(@"队列B数据变更: %d", self.a);
        }
    });
    
    dispatch_async(queue, ^{
        NSLog(@"队列C读取: %d", self.a);
    });
}

错误打印结果


错误示范

很明显并未达到预期, 那么我们加一个栅栏, 改成

    dispatch_async(queue, ^{
        NSLog(@"队列A读取: %d", self.a);
    });
    
    __weak typeof(self) weakSelf = self;
    
//    dispatch_async(queue, ^{
//
//        for (int i = 0; i < 10; i++) {
//            weakSelf.a = weakSelf.a + 1;
//
//            NSLog(@"队列B数据变更: %d", self.a);
//        }
//    });
    
    dispatch_barrier_async(queue, ^{
        
        for (int i = 0; i < 10; i++) {
            weakSelf.a = weakSelf.a + 1;
            
            NSLog(@"栅栏: %d", self.a);
        }
    });
    
    dispatch_async(queue, ^{
        NSLog(@"队列C读取: %d", self.a);
    });
正确结果

可看出这样满足我们的条件


问题10: viewDidLoad中有3组异步并发队列, 每个队列顺序打印10个数, 实现1~30打印

其实这道题也是考察栅栏方法dispatch_barrier_async, 建议先看问题9

如果我们正常写3个并发队列

    dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        //打印1到10
        for (int i = 1; i < 11; i++) {
            NSLog(@"0到10: %d",i);
        }
    });
    
    dispatch_async(queue, ^{
        //打印11到20
        for (int i = 11; i < 21; i++) {
            NSLog(@"11到20: %d",i);
        }
    });
  
    dispatch_async(queue, ^{
        //打印21到30
        for (int i = 21; i < 31; i++) {
            NSLog(@"21到30: %d",i);
        }
    });

打印结果

错误示范

那么这个时候dispatch_barrier_async就可以起到这个作用

    dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        //打印1到10
        for (int i = 1; i < 11; i++) {
            NSLog(@"0到10: %d",i);
        }
    });
    
//    dispatch_async(queue, ^{
//        //打印11到20
//        for (int i = 11; i < 21; i++) {
//            NSLog(@"11到20: %d",i);
//        }
//    });
  
    dispatch_barrier_async(queue, ^{
        //打印11到20
        for (int i = 11; i < 21; i++) {
            NSLog(@"11到20: %d",i);
        }
    });
    
    dispatch_async(queue, ^{
        //打印21到30
        for (int i = 21; i < 31; i++) {
            NSLog(@"21到30: %d",i);
        }
    });
    
正确示范

问题11: 使用GCD实现: A, B, C 三个任务并发, 完成后执行任务D

其实这道题就是考察 dispatch_group_async的使用

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
    NSMutableArray *arr = [NSMutableArray array];
    
    dispatch_group_t group = dispatch_group_create();
    
    for (int i = 0; i < 3; i++) {
        
        dispatch_group_async(group, queue, ^{
           
            NSLog(@"执行: %d 当前数组: %@", i, arr);
            [arr addObject:@(i)];
            
        });

    }
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"全部执行完: %@", arr);
    });
   
}
运行结果
上一篇 下一篇

猜你喜欢

热点阅读