Swift「信号」机制概述
首先,如果你对大中枢派发(GCD)和派发队列不够熟悉的话,请先看 AppCoda 的这篇文章。 在了解了 GCD 内容后,接下来我们来看看 Swift 中的信号机制。
简介先让我们假象一个场景:有一群作者在写作的时候必须共享一支笔来完成个人的工作。很明显在这种情形下,每次都只能有一个人能够进行写作。 在代码世界中,上述场景中写作者就相当于线程而笔就是需要共享的资源(例如:文件、变量、某种权限)。 那么问题来了,如何保证这些共享资源的互斥使用?
共享资源的访问控制对于这些资源的互斥使用问题有人可能会想,只需一个 Bool 类型的 resourceIsAvailable 变量就足够了: if (resourceIsAvailable) { resourceIsAvailable = false useResource() resourceIsAvailable = true } else { // resource is not available,wait or do something else } 但是在多线程并发的情况下(不考虑优先级),我们是无法得知具体是哪个线程在执行上述代码。 示例例如,现在有两个线程 threadA、threadB 都要执行上面的代码,并且对资源的使用是互斥的。那么就可能出现以下情形:
所以,不使用 GCD 就想完成线程安全代码的编写是一件非常困难的事情。
How Semaphores Work:简单来说,分为三个步骤:
当共享资源只有一份并且只能被一个线程占有的时候,那么你可以将上面的 request/signal 理解为对资源的 lock/unlock。
幕后的运行机制The Structure首先信号机制需要一个信号量来控制访问权限,它的组成如下:
Resource Request:wait()当信号机制接受到请求后,它会先去检查自己的资源计数是否大于 0:
Resource Release:signal()当信号机制收到一个使用完毕的释放消息时,他会先去检查请求队列:
Warning: Busy Waiting当线程向信号机制请求资源分配但是没有得到满足时,该线程将会被冻结直到成功获取了资源的使用权。
信号机制在 Swift 中的使用说了那么多,下面我们通过代码来更好的理解该机制。 Declaration信号量结构的声明非常的简单: let semaphore = DispatchSemaphore(value: 1) 其中的参数 value,表示了可供使用的资源总数。 Resource Request请求资源分配也非常的简单: semaphore.wait() 需要注意的是,该信号量并没有给予线程任何物理资,仅仅只是一个使用权限。线程只能在 request 和 release 操作之间对资源进行使用。 一旦线程获得了访问权限,那么我们就可以假定线程一定能够对资源进行正常操作。 Resource Release在释放资源的时候,我们这样写: semaphore.signal() 当完成资源释放后,该线程就无法使用该资源了,除非它再次发起使用请求。 Semaphore Playgrounds与AppCoda 的这篇文章一样,接下来我们看看信号机制的真实使用场景。
在下面的 playgrounds 中会创建两个线程并且两者将赋予不同的优先级,然后执行的时候打印十次 emoji。 非信号机制下的情形import Foundation import PlaygroundSupport let higherPriority = DispatchQueue.global(qos: .userInitiated) let lowerPriority = DispatchQueue.global(qos: .utility) func asyncPrint(queue: DispatchQueue,symbol: String) { queue.async { for i in 0...10 { print(symbol,i) } } } asyncPrint(queue: higherPriority,symbol: " |