SDWebimage相关知识点1-- 线程

线程(thread)是组成进程的子单元,操作系统的调度器可以对线程进行单独的调度。实际上,所有的并发编程 API 都是构建于线程之上的 —— 包括 GCD 和操作队列(operation queues)。

多线程可以在单核 CPU 上同时(或者至少看作同时)运行。操作系统将小的时间片分配给每一个线程,这样就能够让用户感觉到有多个任务在同时进行。如果 CPU 是多核的,那么线程就可以真正的以并发方式被执行,从而减少了完成某项操作所需要的总时间。

NSThread 是 Objective-C 对 pthread 的一个封装。通过封装

直接使用线程可能会引发的一个问题是,如果你的代码和所基于的框架代码都创建自己的线程时,那么活动的线程数量有可能以指数级增长。这在大型工程中是一个常见问题。例如,在 8 核 CPU 中,你创建了 8 个线程来完全发挥 CPU 性能。然而在这些线程中你的代码所调用的框架代码也做了同样事情(因为它并不知道你已经创建的这些线程),这样会很快产生成成百上千的线程。代码的每个部分自身都没有问题,然而最后却还是导致了问题。使用线程并不是没有代价的,每个线程都会消耗一些内存和内核资源。

GCD  Grand Central Dispatch

通过 GCD,开发者不用再直接跟线程打交道了,只需要向队列中添加代码块即可,GCD 在后端管理着一个线程池。GCD 不仅决定着你的代码块将在哪个线程被执行,它还根据可用的系统资源对这些线程进行管理。这样可以将开发者从线程管理的工作中解放出来,通过集中的管理线程,来缓解大量线程被创建的问题。

GCD 带来的另一个重要改变是,作为开发者可以将工作考虑为一个队列,而不是一堆线程,这种并行的抽象模型更容易掌握和使用。

在绝大多数情况下使用默认的优先级队列就可以了。如果执行的任务需要访问一些共享的资源,那么在不同优先级的队列中调度这些任务很快就会造成不可预期的行为。这样可能会引起程序的完全挂起,因为低优先级的任务阻塞了高优先级任务,使它不能被执行。

操作队列(operation queue)是由 GCD 提供的一个队列模型的 Cocoa 抽象。GCD 提供了更加底层的控制,而操作队列则在 GCD 之上实现了一些方便的功能,这些功能对于 app 的开发者来说通常是最好最安全的选择。

NSOperationQueue 有两种不同类型的队列:主队列和自定义队列。主队列运行在主线程之上,而自定义队列在后台执行。在两种类型中,这些队列所处理的任务都使用 NSOperation 的子类来表述。

你可以通过重写 main 或者 start 方法 来定义自己的 operations 。前一种方法非常简单,开发者不需要管理一些状态属性(例如 isExecuting 和 isFinished),当 main 方法返回的时候,这个 operation 就结束了。这种方式使用起来非常简单,但是灵活性相对重写 start 来说要少一些。

如果你希望拥有更多的控制权,以及在一个操作中可以执行异步任务,那么就重写 start 方法:

注意:这种情况下,你必须手动管理操作的状态。 为了让操作队列能够捕获到操作的改变,需要将状态的属性以配合 KVO 的方式进行实现。如果你不使用它们默认的 setter 来进行设置的话,你就需要在合适的时候发送合适的 KVO 消息。

为了能使用操作队列所提供的取消功能,你需要在长时间操作中时不时地检查 isCancelled 属性:

当你定义好 operation 类之后,就可以很容易的将一个 operation 添加到队列中:

NSOperationQueue *queue = [[NSOperationQueue alloc] init];

YourOperation *operation = [[YourOperation alloc] init];

[queue  addOperation:operation];

另外,你也可以将 block 添加到操作队列中。这有时候会非常的方便,比如你希望在主队列中调度一个一次性任务:

[[NSOperationQueue mainQueue] addOperationWithBlock:^{

// 代码...

}];

虽然通过这种的方式在队列中添加操作会非常方便,但是定义你自己的 NSOperation 子类会在调试时很有帮助。如果你重写 operation 的description 方法,就可以很容易的标示出在某个队列中当前被调度的所有操作 。

除了提供基本的调度操作或 block 外,操作队列还提供了在 GCD 中不太容易处理好的特性的功能。例如,你可以通过 maxConcurrentOperationCount 属性来控制一个特定队列中可以有多少个操作参与并发执行。将其设置为 1 的话,你将得到一个串行队列,这在以隔离为目的的时候会很有用。

另外还有一个方便的功能就是根据队列中 operation 的优先级对其进行排序,这不同于 GCD 的队列优先级,它只影响当前队列中所有被调度的 operation 的执行先后。如果你需要进一步在除了 5 个标准的优先级以外对 operation 的执行顺序进行控制的话,还可以在 operation 之间指定依赖关系,如下:

[intermediateOperation addDependency:operation1];

[intermediateOperation addDependency:operation2];

[finishedOperation addDependency:intermediateOperation];

这些简单的代码可以确保 operation1 和 operation2 在 intermediateOperation 之前执行. 对于需要明确的执行顺序时,操作依赖是非常强大的一个机制。它可以让你创建一些操作组,并确保这些操作组在依赖它们的操作被执行之前执行,或者在并发队列中以串行的方式执行操作。

互斥锁

互斥访问的意思就是同一时刻,只允许一个线程访问某个特定资源。为了保证这一点,每个希望访问共享资源的线程,首先需要获得一个共享资源的互斥锁,一旦某个线程对资源完成了操作,就释放掉这个互斥锁,这样别的线程就有机会访问该共享资源了。

属性atomic 表示每次访问该属性都会进行隐式的加锁和解锁操作

在这里有一个东西需要进行权衡:获取和释放锁所是要带来开销的,因此你需要确保你不会频繁地进入和退出临界区段(比如获取和释放锁)。同时,如果你获取锁之后要执行一大段代码,这将带来锁竞争的风险:其它线程可能必须等待获取资源锁而无法工作。这并不是一项容易解决的任务。

互斥锁解决了竞态条件的问题,但很不幸同时这也引入了一些其他问题,其中一个就是死锁。当多个线程在相互等待着对方的结束时,就会发生死锁,这时程序可能会被卡住。

你在线程之间共享的资源越多,你使用的锁也就越多,同时程序被死锁的概率也会变大。这也是为什么我们需要尽量减少线程间资源共享,并确保共享的资源尽量简单的原因之一

优先级翻转问题 、

优先级反转是指程序在运行时低优先级的任务阻塞了高优先级的任务,有效的反转了任务的优先级。

使用不同优先级的多个队列听起来虽然不错,但毕竟是纸上谈兵。它将让本来就复杂的并行编程变得更加复杂和不可预见。如果你在编程中,遇到高优先级的任务突然没理由地卡住了,可能你会想起本文,以及那个美国宇航局的工程师也遇到过的被称为优先级反转的问题。

UIKit 不是线程安全的,推荐只访问主线程,并且甚至是绘图方法他们都没有明确地表示保证线程安全。

在后台使用 UIKit 对象的的危险之处在于“内存回收问题”。要求 UI 对象应该在主线程中被回收,因为在它们的 dealloc方法被调用回收的时候,可能会去改变 view 的结构关系,而如我们所知,这种操作应该放在主线程来进行。

NSArry 这样不可变类是线程安全的

NSMutableArray 是线程不安全的

NSCache 使用一个可变的字典来存储不可变数据,它不仅会对访问加锁,更甚至在低内存情况下会清空自己的内容。

原子属性

一个非原子的 setter 看起来是这个样子的:

- (void)setUserName:(NSString *)userName {

if (userName != _userName) {

[userName retain];

[_userName release];

_userName = userName;

}

}

要是 setUserName: 被并发调用的话会造成麻烦。我们可能会释放 _userName 两次,这回使内存错误,并且导致难以发现的 bug。

对于任何没有手动实现的属性,编译器都会生成一个 objc_setProperty_non_gc(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy) 的调用。在我们的例子中,这个调用的参数是这样的:

objc_setProperty_non_gc(self, _cmd,

(ptrdiff_t)(&_userName) - (ptrdiff_t)(self), userName, NO, NO);`

objc_setProperty 调用的是如下方法:

static inline void reallySetProperty(id self, SEL _cmd, id newValue,

ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)

{

id oldValue;

id *slot = (id*) ((char*)self + offset);

if (copy) {

newValue = [newValue copyWithZone:NULL];

} else if (mutableCopy) {

newValue = [newValue mutableCopyWithZone:NULL];

} else {

if (*slot == newValue) return;

newValue = objc_retain(newValue);

}

if (!atomic) {

oldValue = *slot;

*slot = newValue;

} else {

spin_lock_t *slotlock = &PropertyLocks[GOODHASH(slot)];

_spin_lock(slotlock);

oldValue = *slot;

*slot = newValue;

_spin_unlock(slotlock);

}

objc_release(oldValue);

}

除开方法名字很有趣以外,其实方法实际做的事情非常直接,它使用了在 PropertyLocks 中的 128 个自旋锁中的 1 个来给操作上锁。这是一种务实和快速的方式,最糟糕的情况下,如果遇到了哈希碰撞,那么 setter 需要等待另一个和它无关的 setter 完成之后再进行工作。

@synchonized(self) 更适合使用在你 需要确保在发生错误时代码不会死锁,而是抛出异常的时候。

对于那些肯定应该线程安全的代码(一个好例子是负责缓存的类)来说,一个不错的设计是使用并发的 dispatch_queue 作为读/写锁,并且确保只锁着那些真的需要被锁住的部分,以此来最大化性能。一旦你使用多个队列来给不同的部分上锁的话,整件事情很快就会变得难以控制了。

@property (nonatomic, strong) NSMutableSet *delegates;

// init方法中

_delegateQueue = dispatch_queue_create("com.PSPDFKit.cacheDelegateQueue",

DISPATCH_QUEUE_CONCURRENT);

- (void)addDelegate:(id<PSPDFCacheDelegate>)delegate {

dispatch_barrier_async(_delegateQueue, ^{

[self.delegates addObject:delegate];

});

}

除非 addDelegate: 或者 removeDelegate: 每秒要被调用上千次,否则我们可以使用一个相对简洁的实现方式:

// 头文件

@property (atomic, copy) NSSet *delegates;

- (void)addDelegate:(id<PSPDFCacheDelegate>)delegate {

@synchronized(self) {

self.delegates = [self.delegates setByAddingObject:delegate];

}

}

dispatch_barrier_async和dispatch_barrier_sync

1.比较

* 共同点:他之前的任务会在他之前完成,他之后的任务会等他在执行完后执行

* 不同点:dispatch_barrier_async后面的任务不会等他执行完再 被添加进 队列;dispatch_barrier_sync后面的任务会等他再执行完以后再添加 进队列

* 任务是 先添加进队列,但是并不是一添加进去就开始执行

原文地址:https://www.cnblogs.com/JShuo/p/8678194.html

时间: 2024-10-26 01:10:01

SDWebimage相关知识点1-- 线程的相关文章

SDWebimage相关知识点1-- NSOperation

GCD GCD是最常用的管理并行代码和执行异步操作的Unix系统层的API.GCD构造和管理队列中的任务. 队列是按先进先出(FIFO)管理对象的数据结构.队列类似电影院的售票窗口,票的销售是谁先到谁先服务.在等待线前面的人先去买他们的门票,在其余的后抵达的人之前.队列在计算机科学中是相似的,因为第一个添加到队列的对象也是第一个从队列中删除的对象. 操作队列  NSOperationQueue GCD是一个底层的C的API,它使开发人员能够并行地执行任务.操作队列,另一方面,是高度抽象的队列模型

servlet简介和相关知识点

关于Servlet! 1.servlet简介 sun提供的一种动态web资源开发技术.本质上就是一段java小程序,可以将Servlet加入到Servlet容器中运行. *Servlet容器 -- 能够运行Servlet的环境就叫做Servlet容器. --- tomcat *web容器 -- 能够运行web应用的环境就叫做web容器 --- tomcat 2.用记事本写一个servlet程序 写一个类实现sun公司定义的Servlet接口 将写好的类配置到tomcat中的web应用的web.x

hibernate框架的核心对象和相关知识点

Hibernate架构下图提供了hibernate体系的高层视图: Hibernate全面解决方案: Hibernate核心APIConfiguration负责管理数据库的配置信息.数据库的配置信息包含了Hibernate连接数据库的一些基本信息(hibernate.cfg.xml),Configuration对象可以通过加载配置文件获取并管理这些信息. Configuration config = new Configuration().configure();或者:File file = n

数据库相关知识点(秋招整理)

数据库 1.   数据库事务的 4 个特性是:原子性.一致性.持续性.隔离性 1)   原子性:事务是数据库的逻辑工作单位,它对数据库的修改要么全部执行,要么全部不执行. 2)   一致性:事务前后,数据库的状态都满足所有的完整性约束. 3)   隔离性:并发执行的事务是隔离的,一个不影响一个.如果有两个事务,运行在相同的时间内,执行相同的功能,同一时间仅有一个请求用于同一数据.设置数据库的隔离级别,可以达到不同的隔离效果. 4)   持久性:在事务完成以后,该事务所对数据库所作的更改便持久的保

垂直搜索的相关知识点总结

垂直搜索引擎大体上需要以下技术 1.Spider 2.网页结构化信息抽取技术或元数据采集技术 3.分词.索引 4.其他信息处理技术 垂直搜索引擎的技术评估应从以下几点来判断 1.全面性 2.更新性 3.准确性 4.功能性 垂直搜索的进入门槛很低,但是竞争的门槛很高.没有专注的精神和精湛的技术是不行的.行业门户网站具备行业优势但他们又是没有技术优势的,绝对不要想像着招几个人就可以搞定垂直搜索的全部技术,作为一个需要持续改进可运营的产品而不是一个项目来说对技术的把握控制程度又是垂直搜索成功的重要因素

博客6:磁盘以及创建文件系统的相关知识点

1.linux磁盘的相关知识点  (1)I/O ports:I/O设备地址,实质上是一个缓冲器  (2)块设备:Block,存取单位是"块",例如:磁盘  (3)字符设备:char,存取单位是"字符",例如:键盘  (4)设备文件:关联至一个设备驱动程序,进而能够与之对应硬件设备进行通信(仅有元数据,而无数据)  (5)设备号码:              主设备号:major number,表示设备类型              次设备号:minor number

图像处理程序框架—MFC相关知识点

CDC:Windows使用与设备无关的图形设备环境(DC :Device Context) 进行显示 . MFC基础类库定义了设备环境对象类----CDC类.CDC与CGdiObject的关系 说道CDC类就不能不提一下GdiObject---图形对象类. 在Windows应用程序中,设备环境与图形对象共同工作,协同完成绘图显示工作.就像画家绘画一样,设备环境好比是画家的画布,图形对象好比是画家的画笔.用画笔在画布上绘画,不同的画笔将画出不同的画来.选择合适的图形对象和绘图对象,才能按照要求完成

libgdx相关知识点

Gdx.graphics.setContinuousRendering(false); 设置图像为非连续自动渲染. libgdx相关知识点,布布扣,bubuko.com

Android开发涉及有点概念&amp;相关知识点(待写)

前言,承接之前的 IOS开发涉及有点概念&相关知识点,这次归纳的是Android开发相关,好废话不说了.. 先声明下,Android开发涉及概念比IOS杂很多,可能有很多都题不到的.. 首先由于Android是基于Linux,而Linux又是用c山寨Unix的,但是为什么Android不用开发App,其实我也不懂,我想可能是java比较简单. 同样的,IOS为啥没用java,这个我也想可能是因为IOS是封闭的吧..晕,又废话了..好,开始吧,不过这次很多要查资料了,很多名词.单词不会啊! 首先还