博客> iOS面试题之多线程
iOS面试题之多线程
2019-05-21 22:56 评论:0 阅读:226 折腾贞子
    iOS面试中,多线程的问题会经常遇到,现在我们就来了解一下吧!
    一个线程中任务的执行是串行的,如果要将任务比喻成汽车,那么线程就是运行的轨道。一个线程要执行多个任务,则只能一个一个地按顺序执行这些任务。

    一、多线程
    1、概念
    多线程是指一个进程中可以开启多条线程,每条线程可以并行执行不同的任务。
    其实,所谓的多线程并发,还是只有一条线程在工作,只是CPU快速地在多条线程之间进行切换。只是用户察觉不到而已。
    2、优缺点 
    能适当提高程序的执行效率
    能适当提高资源的利用率(CPU、内存利用率)
    创建线程是有开销的。
    iOS下主要成本包括:内核数据结构、栈空间(主线程1MB,子线程512KB)创建线程大约需要90毫秒的时间。
    如果开启大量的线程会降低程序的性能
    线程越多,cpu在调度线程上的开销就越大 
    3、操作
    主线程又被称为UI线程,一般做一些UI操作,而耗时的操作会丢给子线程在后台中运行。如此一来,用户在对UI进行操作时,就不会出现卡顿的现象。

    -(void)touchesBegan:(NSSet<UITouch> *)touches withEvent:
    (UIEvent *)event {
        for(int i = 0; i < 50000>        
    创建pthread方法,在用户点击时调用
    在这个方法中,创建一个pthread线程。pthread线程中有四个参数,分别代表指向线程代号的指针、线程的属性、指向函数的指针、传递给该函数的参数。

    -(void)touchesBegan:(NSSet<UITouch> *)touches withEvent:(UIEvent *)event{
        /**
            参数1:指向线程代号的指针
            参数2:线程的属性
            参数3:指向函数的指针   void * (*) (void *)
            参数4:传递给该函数的参数
         */
        pthread_t threadID;
        int result =  pthread_create(&threadID, NULL, &demo, NULL);
        if(result == 0){
            NSLog(@"OK");
        }else{
            NSLog(@"error %d",result);
        }
    }
    void * demo(void * param){
        NSLog(@"%@",[NSThread currentThread]);
        return NULL;
    }

    在这段代码中,其实只有传入的&demo是子线程,其他的地方都是主线程执行。并且执行时也不一定总是先执行主线程,因为在CPU来回在线程中进行切换的时候,有可能先执行子线程。所以在多线程开发中,千万不要只相信一次的执行结果。

    2、NSThread
    NSThread是OC的多线程实现方案,使用更加面向对象,可以直接操作线程对象。
    下面是NSThread实现方案的简单案例
    在触摸方法中调用多线程方法。
    在多线程方法中创建一个NSThread线程。在线程开始的时候调用传递给selector的方法,并传递thread参数。
    启动线程

    -(void)touchesBegan:(NSSet<UITouch> *)touches withEvent:(UIEvent *)event{
        [self threadDemo1];
    }
    -(void)threadDemo1{
       // 用NSThread创建线程时要用[thread start]开启线程
        NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(demo:) object:@"Thread"];
        [thread start];
    }
    -(void)demo:(id)obj{
        for(int i = 0; i &lt; 2; i++){
            NSLog(@"%@",[NSThread currentThread]);
        }
    }

    3、GCD
    GCD的全称是Grand Central Dispatch。是属于C语言的函数。旨在替代NSThread等多线程技术。充分利用了设备的多核。其实它并不叫多线程,只是完成了多线程的封装和使用。
    GCD的使用其实就两个简单的步骤:a、定制任务,确定要做的事情 b、将任务添加到队列中
    GCD会自动将队列中的任务取出,放到对应的线程中执行,任务的取出遵循对象的FIFO原则。
    GCD基本案例
    创建一个按钮,当点击时调用点击方法,启动线程
    创建线程时做延时操作,等待线程执行结束再回到主线程中刷新UI

    @interface ViewController ()
    @property(nonatomic,strong)UIButton *btn;
    @end

    -(void)viewDidLoad{
        //点击按钮时执行GCD
        _btn = [UIButton buttonWithType:UIButtonTypeCustom];
        _btn.frame = CGRectMake(100, 200, 100, 30);
        [_btn setTitle:@"GCD" forState:UIControlStateNormal];
        [_btn setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
        [_btn addTarget:self action:@selector(clickGCD) forControlEvents:UIControlEventTouchUpInside];
        [self.view addSubview:_btn];
    }
    - (void)clickGCD{
    //    dispatch_get_global_queue(参数1, 参数2)
    //    参数1:代表优先级
    //    参数2:代表线程创建时是并行还是串行
        NSLog(@"执行GCD");
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"start task 1");
            [NSThread sleepForTimeInterval:3];
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"刷新UI");
            });
        });
    }

    其实GCD的这种写法在开发中也是非常常见的,比如说,在做DB操作的时候,去存储一个数据库;又比如说,在下载一个图片之时,通过dispatch_get_global_queue到子线程中去执行下载,之后再通过dispatch_get_main_queue返回主线程刷新UI。
    解释参数
    dispatch_get_global_queue(参数1,参数2)
    参数1:代表优先级
    // 创建三个任务
    - (void)clickGCD{
        NSLog(@"执行GCD");
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
            NSLog(@"start task 1");
            [NSThread sleepForTimeInterval:2];
            NSLog(@"END TASK 1");
        }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
                NSLog(@"start task 2");
                [NSThread sleepForTimeInterval:2];
                NSLog(@"END TASK 2");
        });
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
            NSLog(@"start task 1");
            [NSThread sleepForTimeInterval:2];
            NSLog(@"END TASK 1");
        });

    优先级设定好了,在上面代码中,本意是让2线程的优先级高,但在控制台上显示的结果却是1线程先执行完成。如何来保证线程的执行顺序呢,此时就需要用到串行来解决了。

    dispatch_queue_t queue = dispatch_queue_create("com.test.gcd.queue",NULL);
    dispatch_async(queue, 0), ^{
            NSLog(@"start task 1");
            [NSThread sleepForTimeInterval:2];
            NSLog(@"END TASK 1");
        });
    dispatch_async(queue, 0), ^{
            NSLog(@"start task 2");
            [NSThread sleepForTimeInterval:2];
            NSLog(@"END TASK 2");
        });
    dispatch_async(queue, 0), ^{
            NSLog(@"start task 3");
            [NSThread sleepForTimeInterval:2];
            NSLog(@"END TASK 3");
        });

    这样就保证了线程的顺序执行。很多小伙伴在面试的时候都会说开辟了三个线程,其实,这样说是不对的。
    其实它是把三个任务都放到了同一个线程中执行。
    参数2:代表线程创建时是并行还是串行

    dispatch_queue_t queue = dispatch_queue_create("com.test.gcd.queue",DISPATCH_QUEUE_CONCURRENT);

    4、NSOperation
    NSOperation实际上是GCD的一种封装,它的实例封装了执行操作已经所需要的数据,并且能以并发或非并发的方式去执行这样的操作。在多线程中,可以将它理解为线程池的概念,当创建NSOperation时,可以将它的对象加入到队列当中。之后系统会根据分配的资源决定要先执行哪个线程。它有两种使用方式。一是通过NSInvocationOperation&NSBlockOperation类去使用它。另一种方式是通过自定义类继承NSOperation。

    三、面试题
    1、Unix系统如何进行通信?
    答:支持三种通信:基本通信用来协调进程间的同步和互斥,管道通信和IPC则都适用于大批量的数据传递。

    2、进程间死锁的原因有哪些?死锁的必要条件又是什么呢?
    答:资源竞争和进程的推进顺序非法造成了死锁。而互斥、请求保存、不可剥夺及环路则是造成死锁的四个必要条件。

    3、如果有a,b,c,d有4个异步请求,如何判断它们都完成执行?
    答:当然是通过GCD的group来实现咯。将4个线程放到group中,当4个异步执行完成后,会回调dispatch_group_notify方法

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);  dispatch_group_t group = dispatch_group_create(); 
    dispatch_group_async(group, queue, ^{ /* a */ }); 
    dispatch_group_async(group, queue, ^{ /* b */ }); 
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{ 
    //回调
    }); 

    4、若想让这4个请求按顺序执行,又当如何?
    答:如果要求顺序执行,只要将创建队列的第二个参数改成串行队列,将任务放到串行队列中,自然就是按顺序来异步执行了。

    5、下面说法错误的是?
    A、GCD的开销要比NSThread大
    B、可以在子线程中修改Ui元素
    C、NSOperationQueue是比NSthread更高层的封装
    D、GCD可以根据不同优先级分配线程
    答:B。Ui元素的更新必须在主线程。GCD与Blcok配合使用,block需要自动捕捉上下文变量信息等,因此需要更多资源。GCD提供了多个优先级,我们可以设置,让它为我们分配线程

    6、为什么有的开发者在做项目中农工喜欢用GCD而不是NSOperation?而国外的大牛们的多线程都是使用NSOperation呢?
    分析:NSOperationQueue用GCD来封装,是GCD的高级抽象。GCD仅仅支持FIFO队列,而NSOperationQueue可以被设置优先级。NSOperationQueue支持KVO,意味着我们可以观察任务的执行状态。
    答:1、NSOperationQueue比GCD更抽象,所以GCD在追求性能上的速度最快。
           2、NSOperationQueue已经内建了异步操作间的事务性、顺序性和依赖关系,而GCD需要自己写代码实现。
           3、若异步操作过程中,更多的交互和UI要被呈现,应选NSOperationQueue。若任务间不太依赖,而需要更高的并发能力,GCD则更有优势。
收藏
0
sina weixin mail 回到顶部