iOS面试-多线程

计算机多进程发展的背景:

早期计算机工作是单道批处理程序处理,讲写好的作业卡依次放进计算机的批处理程序执行,效率非常低,后面出现了多道批处理程序,同一段时间允许多个程序同事快速执行,虽然效率很高,但是后面随着硬件技术的跟进,除了分时系统处理作业这是已经成为是进程作业;根据每个进程的时间分片,同一时间里快速切换执行。后面又出现实时系统,更加高效。。。

多线程概念:

多线程是指在软件或者硬件条件下可以实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件的支持可以在同一时间执行多个线程,进而提高计算机的处理能力。

多线程于进程的关系:

进程是程序执行的基本单元,而线程是程序执行的基本单位,一个进程可以拥有多个线程,并至少拥有一个线程,每个线程独立执行,多个线程可以共享它所在进程的代码、数据、进程空间和对应的堆空间;

多线程对程序开发带来的便利:

  1. 单线程在遇到耗时操作的时候会阻塞UI线程,用户界面的鼠标点击、手势滚动都会立刻进行执行,造成了很不好的用户体验,多线程技术允许将耗时操作放进单独的线程里区执行,从而不会影响UI线程的交互,提高用户体验;
  2. 相对进程来说,多线程的创建、切换所需要cpu消耗、内存占用量都会很少;
  3. 对于多核的操作系统,单一线程无法很好使用的多核资源,造成资源浪费。

多线程引进的问题:

  1. 实现多线程的代码会更加复杂、难度、维护起来难;
  2. 对于模型共享数据来时是多个线程共享的,可能会出现线程死锁的问题;
  3. 由于线程的创建、调度、切换都会消耗一定的cpu计算资源、内存资源、指令调度资源,疯狂的创建多线程会起到适得其反的后果;
  4. 数据不安全,对于同一个共享区域的数据,可能会被篡改;

iOS开发过程中多线程实现的方案:

基于c语言的POSIX线程,成为pthread 线程;是操作系统的多线程实现方案,由于oc中可以实现也可以使用c混编,posix线程在iOS开发中也可以使用;posix线程的优点是较为底层性能会高效,但是由于c语言开发,所以需要我们手动的管理线程的内容,对于开发效率是一种不利;

nsthread 线程基于c语言封装、面向对象的多线程实现方案,api如下:

1.  init 方法实现多线程创建,该方法需要我们手动执行strart指令,将它放进带线程就绪队列,等待系统自己出发;
2. detachNewThreadSelector 方法创建并自动放到线程就绪队列等待系统自动去出发;

2.1 、 nsthread 优点:

1. nsthread提供适合oc开发人员使用多线程实现api,降低开发门槛;
2. nsthread 提供了一套工具酷,我们可以快捷拿到多线程的信息和状态,例如: 是否是主线程、是否被取消,是否正在运行等

2.2 、nsthread 缺点

1. nsthread 的最大缺点是线程之间的通信比较麻烦,也是设计的一种缺陷;

GCD

  1. 概念

gcd 我们称为中央调度管理,基于c接口,是apple公司为了线程不能充分利用多核资源引入的一个多线程实现方案;它的工作流程是将一个事件分成若干小任务放入串行或者并发执行队列,遵循先进先出的规则去加入线程执行单元,然后执行其同步或者异步的执行;
1.1 : 优点:

高效: 基于mach内核线程调度;
线程安全:不用开发者关心线程的内存问题;
使用简单,少代码解决大问题;
  1. 核心机制-队列

gcd 的核心内容是队列
gcd 的队列分为: 串行、并发、主队列、全局队列、组队列

  1. 串行/并发/同步异步 组合方式对应的结果
 1. 主线程的时间优先
 2. 串行同步执行: 执行完一个任务,再执行下一个任务。不开启新线程,都在主线程执行。
 3. 串行异步执行: 开启新线程,但因为任务是串行的,所以还是按顺序执行任务。
 4. 并发队列异步执行: 并发执行,会开启新的线程。
 5. 并发队列同步执行; 按顺序执行,不会开启新的线程。
 6. 主队列同步事件: 
 7. 主队列异步事件:  按次序执行。
  1. gcd 的特别函数:
    4.1 栏栅函数 dispatch_barrier_(a)sync

当我们需要异步事件同步去执行的时候我们需要用函数 dispatch_barrier_sync
dispatch_barrier_async 异步执行

4.2 信号量

dispatch_semaphore 为了解决共享资源竞争问题,可以用信号量来决定资源是否可以被访问。
dispatch_semphore_create 创建一个信号量,参数是最大可以同事访问的线程;
dispatch_semphore_waite 对信号量执行减1操作,如果结果大于0,就可以直接进行资源访问;
dispatch_semphone_sign 对信号变量执行加一操作,访问完成就要释放对资源的占用权利,即信号量加1,不影响其他线程访问。

4.3 延迟执行函数

dispatch_after_t 函数 参数: dispatch_time_t dispatch_queue_t block

4.4 组变量
当我们需要多个请求后才能进入下一步时,我们需要下面的函数\

dispatch_group_t

1. 创建一个全局的队列,默认优先级
2. 创建一个group_t 
3. 创建group任务加入到全局队列
4. 监听组队列完成的notify dispatch_group_notify

// 加载图片例子:
   NSLog(@"全部开始-----%@", [NSThread currentThread]);
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    dispatch_group_async(group, queue, ^{
        sleep(4);
        NSLog(@"子线程1-----%@", [NSThread currentThread]);
    });
    
    dispatch_group_async(group, queue, ^{
        sleep(3);
        NSLog(@"子线程2-----%@", [NSThread currentThread]);
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"全部结束-----%@", [NSThread currentThread]);
    });

4.5 dispatch_group_enter/dispatch_group_leave 函数

4.4的任务是组任务力里只是简单的输出一句话,如果在里面加入一个异步请求就出出现问题:

有问题的代码:

NSLog(@"全部开始-----%@", [NSThread currentThread]);
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("com.example.gcdDemo", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_group_async(group, queue, ^{
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            sleep(4);
            NSLog(@"模拟请求1-----%@", [NSThread currentThread]);
        });
    });
    
    dispatch_group_async(group, queue, ^{
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            sleep(3);
            NSLog(@"模拟请求2-----%@", [NSThread currentThread]);
        });
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"全部结束-----%@", [NSThread currentThread]);
    });

它打印log: 全部开始-全部结束-任务2,任务1, 我们发现这样的话与我们的要求的不一样,我们要求的是所有的任务都结束才会执行notify 函数但是它提前执行啦。

为了解决上面的问题需要用到 dispatch_group_enter/dispatch_group_leave
在组组任务内部,每次进行异步操作之前,先执行 dispatch_group_enter函数,参数是group变量; 当内部的任务完成时执行dispatch_group_leave()
代码如下:

NSLog(@"全部开始-----%@", [NSThread currentThread]);
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("com.example.gcdDemo", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_group_async(group, queue, ^{
        dispatch_group_enter(group);
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            sleep(4);
            NSLog(@"模拟请求1-----%@", [NSThread currentThread]);
            dispatch_group_leave(group);
        });
    });
    
    dispatch_group_async(group, queue, ^{
        dispatch_group_enter(group);
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            sleep(3);
            NSLog(@"模拟请求2-----%@", [NSThread currentThread]);
            dispatch_group_leave(group);
        });
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"全部结束-----%@", [NSThread currentThread]);
    });

4.6 dispatch_apply 快苏迭代函数 参数: 迭代的次数,所在的队列,任务

4.7 dispatch_once : 只执行一次任务,通常用在单例,

5.gcd的常见问题
5.1 线程死锁问题
原因:队列引起- 场景:在主线程里执行同步将任务添加到主队列里
例子: 加入在viewdidload 方法中 我们同步执行将block(执行一句日志)添加祝队列,在这易操作后面加上一个结束日志,运行发现,block的日志、结束日志都没有打印,通过现象说明是block来不及执行、结束日志也无法执行。原因是,首先程序首先将同步执行函数加入到线程执行单元里,然后根据同步执行函数要求将block加入到执行主队列里,由于是同步函数,只能等到block结束,同步函数才能返回,而block在主队列,它的机制是只有上一个出队列的任务完成它才可以出队列,这样同步函数不能返回导致结束就无法按顺序打印,block无法执行,导致同步函数无法返回,系统无法进入下一步,就发错-死锁。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,723评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,003评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,512评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,825评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,874评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,841评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,812评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,582评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,033评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,309评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,450评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,158评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,789评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,409评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,609评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,440评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,357评论 2 352

推荐阅读更多精彩内容