IOS 线程死锁详细介绍

iOS线程死锁

前言:

在chat view的开发过程中,添加了“混合标签添加与显示”,app出现发送图片会出现卡死的情况,但过了大约30~40 second后会恢复正常。

问题分析:

因为没有任何报错与提示,只能根据表面现象慢慢分析,经过多次测试与观察得出以下规律:

(1)发送表情与文本不会发生该情况,只有发送图片才会发生app界面卡死的情况。(主线程阻塞,与大文件上传有关)
     (2)app卡死一定时间后会恢复正常,但时间不定,大约范围在30~40 second。(主线程解除阻塞,与系统某些机制有关)
     (3)当界面中有gif图时才会发生,界面中全是移动端本地图片是能顺利发送。(与gif下载有关)

根据上述现象,可以总结为:主线程阻塞,过后因通信通信失败而阻塞解除。

因为与gif图的下载有关,于是分析gif的下载实现,gif图的下载由以下代码实现。

NSData *gifImageData = [NSDatadataWithContentsOfURL:model.imageRemoteURL];
        FLAnimatedImage *FLAImage = [FLAnimatedImageanimatedImageWithGIFData:gifImageData];

其中,关于NSData的静态方法dataWithContentsOfURL的说明中有以下使用说明。

Important
Do not use this synchronous method to request network-based URLs. For network-based URLs, this method can block the current thread for tens of seconds on a slow network, resulting in a poor user experience, and in iOS, may cause your app to be terminated.

这里说明了要尽量避免使用dataWithContentsOfURL从网络下载资源,因为它会阻塞当前线程,若下载的文件比较大而网络又不好就会带来很差的用户体验,对于iOS设备还可能引起app被迫终止。

虽然只需要把dataWithContentsOfURL放置到别的线程中实现,又或者使用NSURLConnect、NSURLSession中的异步请求方法也能解决问题,但本质上引起这问题是因为调用dataWithContentsOfURL时网络环境差吗?

明显不是,因为测试环境是模拟器,模拟器的cpu、网络通信是比真机要好的,只有GPU是不如真机。而且问题的出现规律是固定。

但dataWithContentsOfURL已经解析了为什么主线程阻塞,那么现在需要解析为什么会阻塞那么长的时间。

CPU分析结果:
     

图中的标记的那段是发生异常情况时的CPU的情况。从CPU图上可以更加肯定线程是并没有死掉的,而且进入了无法响应的状态,因此可以判断出发生线程死锁的情况。

线程死锁     

死锁的概念:

死锁是进程死锁的简称,是由Dijkstra于1965年研究银行家算法时首先提出来的。它是计算机操作系统乃至并发程序设计中最难处理的问题之一。实际上,死锁问题不仅在计算机系统中存在,在我们日常生活中它也广泛存在。

我们先看看这样一个生活中的例子:在一条河上有一座桥,桥面较窄,只能容纳一辆汽车通过,无法让两辆汽车并行。如果有两辆汽车A和B分别由桥的两端驶上该桥,则对于A车来说,它走过桥面左面的一段路(即占有了桥的一部分资源),要想过桥还须等待B车让出右边的桥面,此时A车不能前进;对于B车来说,它走过桥面右边的一段路(即占有了桥的一部分资源),要想过桥还须等待A车让出左边的桥面,此时B车也不能前进。两边的车都不倒车,结果造成互相等待对方让出桥面,但是谁也不让路,就会无休止地等下去。这种现象就是死锁。如果把汽车比做进程,桥面作为资源,那麽上述问题就描述为:进程A占有资源R1,等待进程B占有的资源Rr;进程B占有资源Rr,等待进程A占有的资源R1。而且资源R1和Rr只允许一个进程占用,即:不允许两个进程同时占用。结果,两个进程都不能继续执行,若不采取其它措施,这种循环等待状况会无限期持续下去,就发生了进程死锁。

上图是一张描述一个简单的死锁模型的图,首先Thread1锁定占有资源Y,Thread2锁定占有资源X,但同时相互向对方请求资源,因此都无法释放自身资源去访问下一个资源,结果两个线程相互阻塞。

死锁的四个充分必要条件

从以上分析可见,如果在计算机系统中同时具备下面四个必要条件时,那麽会发生死锁。换句话说,只要下面四个条件有一个不具备,系统就不会出现死锁。

〈1〉互斥条件。即某个资源在一段时间内只能由一个进程占有,不能同时被两个或两个以上的进程占有。这种独占资源如CD-ROM驱动器,打印机等等,必须在占有该资源的进程主动释放它之后,其它进程才能占有该资源。这是由资源本身的属性所决定的。如独木桥就是一种独占资源,两方的人不能同时过桥。

〈2〉不可抢占条件。进程所获得的资源在未使用完毕之前,资源申请者不能强行地从资源占有者手中夺取资源,而只能由该资源的占有者进程自行释放。如过独木桥的人不能强迫对方后退,也不能非法地将对方推下桥,必须是桥上的人自己过桥后空出桥面(即主动释放占有资源),对方的人才能过桥。

〈3〉占有且申请条件。进程至少已经占有一个资源,但又申请新的资源;由于该资源已被另外进程占有,此时该进程阻塞;但是,它在等待新资源之时,仍继续占用已占有的资源。还以过独木桥为例,甲乙两人在桥上相遇。甲走过一段桥面(即占有了一些资源),还需要走其余的桥面(申请新的资源),但那部分桥面被乙占有(乙走过一段桥面)。甲过不去,前进不能,又不后退;乙也处于同样的状况。

〈4〉循环等待条件。存在一个进程等待序列{P1,P2,...,Pn},其中P1等待P2所占有的某一资源,P2等待P3所占有的某一源,......,而Pn等待P1所占有的的某一资源,形成一个进程循环等待环。就像前面的过独木桥问题,甲等待乙占有的桥面,而乙又等待甲占有的桥面,从而彼此循环等待。

上面我们提到的这四个条件在死锁时会同时发生。也就是说,只要有一个必要条件不满足,则死锁就可以排除。

项目中死锁的形成与解决

根据以上理论,以及断点调试,可以分析得项目的形成,如下图所描述。

因此只要让同步下载时不会挂起主线程就可以避免线程死锁的情况发生。因此项目中使用NSURLConnet的同步请求方法实现下载即可, 因为它不会挂起主线程,还能让其他线程往主线程中添加代码块block。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

(0)

相关推荐

  • 在IOS中为什么使用多线程及多线程实现的三种方法

    多线程是一个比较轻量级的方法来实现单个应用程序内多个代码执行路径. 在系统级别内,程序并排执行,程序分配到每个程序的执行时间是基于该程序的所需时间和其他程序的所需时间来决定的. 然而,在每个程序内部,存在一个或者多个执行线程,它同时或在一个几乎同时发生的方式里执行不同的任务. 概要提示: iPhone中的线程应用并不是无节制的,官方给出的资料显示,iPhone OS下的主线程的堆栈大小是1M,第二个线程开始就是512KB,并且该值不能通过编译器开关或线程API函数来更改,只有主线程有直接修改UI

  • 详解iOS 多线程 锁 互斥 同步

    在iOS中有几种方法来解决多线程访问同一个内存地址的互斥同步问题: 方法一,@synchronized(id anObject),(最简单的方法) 会自动对参数对象加锁,保证临界区内的代码线程安全 @synchronized(self) { // 这段代码对其他 @synchronized(self) 都是互斥的 // self 指向同一个对象 } 方法二,NSLock NSLock对象实现了NSLocking protocol,包含几个方法: lock,加锁 unlock,解锁 tryLock

  • iOS开发网络篇—实现大文件的多线程断点下载

    说明:本文介绍多线程断点下载.项目中使用了苹果自带的类,实现了同时开启多条线程下载一个较大的文件.因为实现过程较为复杂,所以下面贴出完整的代码. 实现思路:下载开始,创建一个和要下载的文件大小相同的文件(如果要下载的文件为100M,那么就在沙盒中创建一个100M的文件,然后计算每一段的下载量,开启多条线程下载各段的数据,分别写入对应的文件部分). 项目中用到的主要类如下: 完成的实现代码如下: 主控制器中的代码: #import "YYViewController.h" #import

  • 详解iOS多线程之2.NSThread的加锁@synchronized

    那什么时候需要加锁呢,就是当多条线程同时操作一个变量时,就需要加锁了. 上代码 声明变量 @interface ViewController () @property (strong, nonatomic)NSThread *thread1; @property (strong, nonatomic)NSThread *thread2; @property (strong, nonatomic)NSThread *thread3; @property (assign, nonatomic)int

  • IOS多线程实现多图片下载(一)

    在没有步入正文之前先给大家展示下效果图,如果大家觉得很满意请继续往下阅读全文. 大家可以看到这个界面很简单,其实就是UITableView的布局,但是难点是在于如何从网上下载这些图片,下载之后应如何进行存储! 我们一步一步进行解析,先从单线程(主线程)进行多图片下载我们布局上的文字及图片的地址从plist文件中进行读取 根据结构,我们自定义一个数据模型文件 DDZApp.h #import <Foundation/Foundation.h> @interface DDZApp : NSObje

  • 详解iOS中多线程app开发的GCD队列的使用

    GCD的基本使用 一.主队列介绍 主队列:是和主线程相关联的队列,主队列是GCD自带的一种特殊的串行队列,放在主队列中得任务,都会放到主线程中执行. 提示:如果把任务放到主队列中进行处理,那么不论处理函数是异步的还是同步的都不会开启新的线程. 获取主队列的方式: 复制代码 代码如下: dispatch_queue_t queue=dispatch_get_main_queue(); (1)使用异步函数执行主队列中得任务,代码示例: 复制代码 代码如下: // //  YYViewControll

  • IOS 创建并发线程的实例详解

    IOS 创建并发线程的实例详解 创建并发线程 主线程一般都是处理UI界面及用户交互的事儿的.其他的事一般就要另外的线程去处理,如下载,计算等... 现在先简单创建3个线程,分别打印出1-1000,,为了方便,线程3就放在主线程中执行. - (void) firstCounter{ @autoreleasepool { NSUInteger counter = 0; for (counter = 0; counter < 1000; counter++){ NSLog(@"First Cou

  • IOS 线程死锁详细介绍

    iOS线程死锁 前言: 在chat view的开发过程中,添加了"混合标签添加与显示",app出现发送图片会出现卡死的情况,但过了大约30-40 second后会恢复正常. 问题分析: 因为没有任何报错与提示,只能根据表面现象慢慢分析,经过多次测试与观察得出以下规律: (1)发送表情与文本不会发生该情况,只有发送图片才会发生app界面卡死的情况.(主线程阻塞,与大文件上传有关)      (2)app卡死一定时间后会恢复正常,但时间不定,大约范围在30-40 second.(主线程解除

  • iOS开发系列--详细介绍数据存取

    概览 在iOS开发中数据存储的方式可以归纳为两类:一类是存储为文件,另一类是存储到数据库.例如前面IOS开发系列-Objective-C之Foundation框架的文章中提到归档.plist文件存储,包括偏好设置其本质都是存储为文件,只是说归档或者plist文件存储可以选择保存到沙盒中,而偏好设置系统已经规定只能保存到沙盒的Library/Preferences目录.当然,文件存储并不作为本文的重点内容.本文重点还是说数据库存储,做过数据库开发的朋友应该知道,可以通过SQL直接访问数据库,也可以

  • IOS 身份证校验详细介绍及示例代码

    IOS 身份证校验 身份证基础知识: 身份证是国民的身份编号,编号是有一定规律的,这里介绍身份证验证规则比较详细.项目中经常会需要对身份证进行校验,我们先了解一些基本知识,然后分析代码 居民身份证号码,根据[中华人民共和国国家标准 GB 11643-1999]中有关公民身份号码的规定,公民身份号码是特征组合码,由十七位数字本体码和一位数字校验码组成.排列顺序从左至右依次为:六位数字地址码,八位数字出生日期码,三位数字顺序码和一位数字校验码. 居民身份证是国家法定的证明公民个人身份的有效证件. 结

  • java 线程同步详细介绍及实例代码

    java 线程同步 概要: 为了加快代码的运行速度,我们采用了多线程的方法.并行的执行确实让代码变得更加高效,但随之而来的问题是,有很多个线程在程序中同时运行,如果它们同时的去修改一个对象,很可能会造成讹误的情况,这个时候我们需要用一种同步的机制来管理这些线程. (一)竞争条件 记得操作系统中,让我印象很深的有一张图.上面画的是一块块进程,在这些进程里面分了几个线程,所有这些线程齐刷刷统一的指向进程的资源.Java中也是如此,资源会在线程间共享而不是每个线程都有一份独立的资源.在这种共享的情况下

  • IOS property属性详细介绍使用注意事项

    IOS property属性 原子性 atomic nonatomic 访问权限 readWrite readOnly 内存管理 strong retain copy assign weak unsafe_unretained 方法命名 getter = <name> setter = <name> 结论 1.原子性 (1)atomic 原子性,可以理解成就是生成getter.setter方法时,编译器会自动为程序添加同步锁,减少因为在不同的线程中同时对同一个内存地址进行读写操作,

  • Python线程指南详细介绍

    本文介绍了Python对于线程的支持,包括"学会"多线程编程需要掌握的基础以及Python两个线程标准库的完整介绍及使用示例. 注意:本文基于Python2.4完成,:如果看到不明白的词汇请记得百度谷歌或维基,whatever. 1. 线程基础 1.1. 线程状态 线程有5种状态,状态转换的过程如下图所示: 1.2. 线程同步(锁) 多线程的优势在于可以同时运行多个任务(至少感觉起来是这样).但是当线程需要共享数据时,可能存在数据不同步的问题.考虑这样一种情况:一个列表里所有元素都是0

  • iOS 对象属性详细介绍

    iOS 对象属性 oc对象的一些属性: retain,strong, copy,weak,assign,readonly, readwrite, unsafe_unretained 下面来分别讲讲各自的作用和区别: retain,计数器加1, (增加一个指向内存的指针) 对应release(计数器-1) setter 方法对参数进行 release 旧值再 retain 新值,所有实现都是这个顺序 - (void)setBackView:(UIView *)backView { if (_bac

  • IOS Cache设计详细介绍及简单示例

    IOS Cache设计 Cache的设计是个基础计算机理论,也是程序员的重要基本功之一.Cache几乎无处不在,CPU的L1 L2 Cache,iOS系统的clean page和dirty page机制,HTTP的tag机制等,这些背后都是Cache设计思想的应用. 为什么需要Cache Cache的目的是为了追求更高的速度体验,Cache的源头是两种数据读取方式在成本和性能上的差异. 在开始着手设计Cache之前,需要先理清数据存储的媒介.作为客户端开发人员来说,我们所关注的数据存储方式也有不

  • java 线程锁详细介绍及实例代码

    java 线程锁 在Java线程中运用synchronized关键字来达到同步的 synchronized可以锁方法,锁类,锁对象,锁代码块 方法锁 // 加在方法上面的同步锁是this public synchronized void print() { System.out.println("同步方法"); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } 类锁

  • C++线程安全容器stack和queue的使用详细介绍

    目录 线程安全的容器栈threadsafe_stack 线程安全的容器队列threadsafe_queue 要构建线程安全的数据结构, 关注几点: 若某线程破坏了数据结构的不变量, 保证其他线程不能看到 提供的操作应该完整,独立, 而非零散的分解步骤避免函数接口固有的条件竞争(比如之前提到的empty和top和pop) 线程安全的容器栈threadsafe_stack 入门(3)里曾介绍过线程安全的stack容器, 这里把代码搬过来再分析 逐项分析, 该代码如何实现线程安全的 template<

随机推荐