Swift「信号」机制概述

前端之家收集整理的这篇文章主要介绍了Swift「信号」机制概述前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

作者:Federico Zanetello 翻译:BigNerdCoding,原文链接

首先,如果你对大中枢派发(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 都要执行上面的代码,并且对资源的使用是互斥的。那么就可能出现以下情形:

  • threadA 首先执行了条件判断语句,并且得到的资源的访问权限。

  • 但是在执行权限锁定的代码之前( resourceIsAvailable = false),处理器切换到 threadB 并且也执行了条件判断语句。

  • 现在两个线程都有了访问权限,这就导致了很严重的问题。

所以,不使用 GCD 就想完成线程安全代码的编写是一件非常困难的事情。

How Semaphores Work:

简单来说,分为三个步骤:

  1. 当你需要使用共享资源的时候,首先给型号机制发送权限请求(request)。

  2. 当信号机制对你开启绿灯放行的时候,我们就可以确保当前资源已经能够被我们使用。

  3. 当资源使用完毕后,你必须给信号机制发送通知signal),让它回收权限并再次分派给其他线程。

当共享资源只有一份并且只能被一个线程占有的时候,那么你可以将上面的 request/signal 理解为对资源的 lock/unlock

幕后的运行机制

The Structure

首先信号机制需要一个信号量来控制访问权限,它的组成如下:

  • 一个计数器 counter 用于标记可用资源数,也就是说它表示了当前还有多少资源还能被线程使用。

  • 一个 FIFO 的线程派发队列,用于处理等待资源访问权限的线程。

Resource Request:wait()

当信号机制接受到请求后,它会先去检查自己的资源计数是否大于 0:

  • 如果大于0,则资源计数减 1 ,并将资源分配给请求者使用。

  • 如果不满足,则将该请求线程放到请求队列的最后。

Resource Release:signal()

当信号机制收到一个使用完毕的释放消息时,他会先去检查请求队列:

  • 如果请求队列里的线程不为空的话,则将队列中的第一个线程移出并将资源分配给该线程。

  • 否则则增加资源计数。

Warning: Busy Waiting

当线程向信号机制请求资源分配但是没有得到满足时,该线程将会被冻结直到成功获取了资源的使用权。

⚠️ 如果该线程是主线程的话,那么整个 App 都将会被冻结失去响应。

信号机制在 Swift 中的使用

说了那么多,下面我们通过代码来更好的理解该机制。

Declaration

信号量结构的声明非常的简单:

let semaphore = DispatchSemaphore(value: 1)

其中的参数 value,表示了可供使用的资源总数。

Resource Request

请求资源分配也非常的简单:

semaphore.wait()

需要注意的是,该信号量并没有给予线程任何物理资,仅仅只是一个使用权限。线程只能在 requestrelease 操作之间对资源进行使用。

一旦线程获得了访问权限,那么我们就可以假定线程一定能够对资源进行正常操作。

Resource Release

在释放资源的时候,我们这样写:

semaphore.signal()

当完成资源释放后,该线程就无法使用该资源了,除非它再次发起使用请求。

Semaphore Playgrounds

与AppCoda 的这篇文章一样,接下来我们看看信号机制的真实使用场景。

因为 Swift Playgrounds 并不能完美支持,所以这里我们使用的是 Xcode Playgrounds。希望 WWDC17 中苹果能够对 Swift Playgrounds 进行功能提升吧。

在下面的 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: " 原文链接:https://www.f2er.com/swift/321515.html

猜你在找的Swift相关文章