【译】iOS 并发与多线程完整指南

  • 原文地址:The Complete Guide to Concurrency and Multithreading in iOS
  • 原文作者:Varga Zolt
  • 译文出自:翻译方案
  • 本文永久链接:gith手机号最旺财的尾数ub.cnodejs安装om/xitu/nodejs和vue的关系gold-m…
  • 译者:YueYong
  • 校对者:haiyang-tju、D执行上下文ylanXie123

iOS 并发与手机号最旺财的尾数多线程完整指南

主线程 vs 后台线程。 Async/await 和 Actor。 GCD vs 操作行列(Operation后端是什么工作Queue) 分Node.js组调度(Group dispatch),如何授权后台线程,以及其他

阐明

在本文中,咱们将会学习以下上下文语境内容:

TABLE OF CONTENTS
What Is Multithreading
  Serial Queue vs. Concurrent Queue
  Parallelism
  Concurrency
Basics of Multithreading
  Main Thread (UI Thread) vs. Background Thread (Global Thread)
GCD (Grand Central Dispatch)
DispatchGroup
DispatchSemaphore
DispatchWorkItem
Dispatch Barrier
AsyncAfter
(NS)Operation and (NS)OperationQueue
DispatchSource (How To Handle Files and Folders)
Deadlock (Issue What To Avoid)
Main Thread Checker (How To Detect Thread Issues)
Threads in Xcode (How To Debug Threads)
Async / Await / Actor Is iOS13+

本文触及较多主题。假如有些内容你现已很了解了,能够测验跳过它,阅读你还未了解的部分手机克隆。会有一些技巧和提示。

实践国际中的多线程实例

假定你有一个餐厅,服务员在收集订单。厨房在预备食物,酒保在制作咖优先级是什么意思啡和鸡尾酒。上下文什么意思

有时,会有许多人点咖啡和食物这些需求许多时刻来预备的东西。此刻铃声突然响起,5 份食物和 4 份咖啡现已预备完Node.js结。但是,即便一切的产品都已备齐,服务员也要逐一送到不同的餐桌上。这便是串行行列。运用串行行列,你只能有一个服务上下文切换员。

现在假定你有两个或三个服务员。他们能够在同一时刻以更快的速度为各桌服务。这便是并行。即,运用多个 CPU 来操作多个进程。

继续假定,一个服务上下文无关文法员不只服务一个桌子,而是先为一切桌子的人送上一切的咖啡,然后,他们会为一些新来上下文字间距怎么调桌子的nodejs和js的区别顾客进行点单,最终才供应一切的食物。这个概念被称为并发。即在同一时刻段内进行上下文切换,办理以及多核算使命。这并不一定意味着它们会在同一时刻都在运转。例如,在后端是什么工作单核机器上进行的多使命处理。

现在的设备都有多个 CPU(中央处理器)。为了能够创建手机克隆具有无缝流转的应用程序,咱们需求了解多线程的概念。这是应用程序中处理多使命的一种办法。很重要的一点是,咱们需求明白,假如某个东西 “有优先级英文用”,它或许不是最好的、最理想的办法。许多时分,我看到一个使命长时刻运转在 UI 线程上,并且阻塞了应用程序几秒钟。现在,关于用户来说,这或许是一个挑选的时刻。用户或许会删去你的应用程序,由于他们觉得其他的应用程序发动得更快,能够更快地获取书本、下载音乐等。竞赛激烈,人们都追求着更优异的体会。

假如 “Serial 6” 的使命是在当时时刻安排的,那么你就能够看到它了。它nodejs是前端还是后端将以 FIFO 的办法添加到列表中,并等候将来的履行。

多线程的基础

我一向纠结的一件事是,没有规范的术语后端开发工程师。为了协助处理这个问题,关于这些主题,我将首要写下近义词、比方。假如你运用 iOS 以外的其他技术,你依然能够了解这个概念并将其延伸到其他技术的上下文无关文法了解与运用上,由于基本原理是相同的。幸运的是,在我职业生涯的早期,我一向在运用 C、C++、C#、node.js、Java(Android) 等技术栈后端,所以我现已习气了这种上下文的切换。

  • 主线程 / UI 线程: 这是一个预先界说好的串行线程,与应用程序一起发动。它监听用户交互和用户界面的改变。一切的改变都需求一个即时的呼应。需求留意的是,不要让这个线程履行较多的耗时使命,由于应用程序会卡死。
DispatchQueue.main.async {
    // Run async code on the Main/UI Thread. E.g.: Refresh TableView
}
  • 后台线程(全局):手机耗电速度过快怎么办界说的。大nodejs和vue的关系多数状况下,咱们依据自己的需求在新线程上创建使命。例如,假如咱们需求下载体积较大的图片,这是在后台线程上完结的。又例如当调用一些 API 时,咱们不想由于等候这个使命的完结而阻止用户的操作。咱们将在后台线程上调上下文无关文法用一个 API 来获取后端开发需要学什么一个电影数据的列表。当收到呼应并完结数据解析后,咱们便切换至主线程上后端并更新用户界nodejs是前端还是后端面。
DispatchQueue.global(qos: .background).async {
     // Run async on the Background Thread. E.g.: Some API calls.
}

在上面的图片中,咱们在第 56 行添加了一个断点。当它被履行到时,应上下文字间距怎么调用程序将会中止,咱们能够在左边的线程信息上下文切换面板上看到这一点。

  1. 你能够看到调度行列(Di优先级队列spatchQueue)的界说(label: “com.kraken.serial”)。 label 是该行列的标识符。
  2. 这些按钮能够用来关闭/过滤掉系统办法调用,只看到用户发起的办法。
  3. 你能够看到,咱们添加了sleep(1)。这段代码将会使应用程序中止 1 秒。
  4. 假如你看了这个指令,它依然是以串行办法触nodejs和vue的关系发的。

依据之前的 iOS,最常用的两个术语之一是串行行列和并发行列。

  1. 这便是并发行列的成果之一。你能够看到上面的串行/主线程也是这样的(com.apple.main-thread)。上下文字间距怎么调
  2. sleep(2)被添加到这个断点上。
  3. 整个过程没有任何次序。它是在后台线程上以异步办法完结的。
let mainQueue = DispatchQueue.main
let globalQueue = DispatchQueue.global()
let serialQueue = DispatchQueue(label: “com.kraken.serial”)
let concurQueue = DispatchQueue(label: “com.kraken.concurrent”, attributes: .concurrent)

咱们还能够创nodejs安装步骤建一个私有行列,它也能够是串行和并发的。

GCD后端语言 (Grand Central Di手机管家spatch上下文字间距怎么调)

GCD 是苹果的低级线程接口,用于支撑多核硬件上的并发代码履行。GCD 以一种简略的办法,让你的手机能够在后台下载视优先级越小越优先吗频的一起坚持用户界面的呼应。

“DispatchQueue 是一个办理应用程序的优先级主线程或后台线程使命的串行或并发履行的目标。” —— Appnodejs和java性能le Developer

执行上下文如你留意到上面的代手机码示例,你能够看到 “qos” 这个词。它指的是服务质量。经过这个参数,咱手机淘宝们能够界说如下的优先级

  • background — 当一个使命对时刻不上下文切换灵敏,或许当用户能够在这个过程中做一些其他的互动时,咱们能够运用这个办法。比方预先获取一些图片做预加载,或许在后台处后端开发是干什么的理一些数据。这个使命的履行需求一定的时刻,几秒或许几分钟优先级是什么意思,乃至几个小时。
  • utility — 长时刻运转的使命。一些用户能够看到处理过程。例如,下载一些带有指标的地图。这个使命或许需求几秒钟乃至几十分钟的时刻。
  • userIniti手机管家ated — 用户从用户界面发动一些使命并等候成果以继续与应用程序交互。这个使命需求几秒钟或一会儿。
  • userInteractive — 用户需求当即完结某些使命,以便后端开发工程师能够继续与应用程序进行下一次交互。是一个即时使命。

符号 “DispatchQueue “很有用。这能够协助咱们在需求时辨认不同的线程类型。

调度组(DispatchGroup)

一般咱们需求发动多个异步进程,但当一切进程完结后,咱们只需求一个事情。这能够经过 Disp后端语言atchGroup 来完结。

“作为一个单元监控的一组使命。nodejs和js的区别” —— Apple Docs后端工程师

例如,有时分在应用程序预备好与用户互动或在主线程上更新用户界面之前,你需求在后台线程上进行多个 API 调用。这里有一些示例代码。

// 1. Create Dispatch Group
let group = DispatchGroup()
// 2.a. Long running Task 1
group.enter()
runLongRunningTask1(completion: {
    print("DispatchGroup: Long running Task 1 finished")
    group.leave()
})
// 2.b. Long running Task 2
group.enter()
runLongRunningTask2(completion: {
    print("DispatchGroup: Long running Task 2 finished")
    group.leave()
})
// 2.b. Long running Task 3
group.enter()
runLongRunningTask3(completion: {
    print("DispatchGroup: Long running Task 3 finished")
    group.leave()
})
// 3. When all are finished Notify
let queueType = DispatchQueue.global(qos: .userInitiated)
group.notify(queue: queueType) {
    print("DispatchGroup - notify: All task Finished.")
}
  • 过程 1. 新建 DispatchGroup
  • 过程 2. 然后,关于该组需求为每个使命调用 gro手机怎么定位别人手机up.enter() 事情来发动使命。
  • 过程 3. 关于每一个 group.enter(手机耗电快怎么办) 都需求在使命完结后调用 group.leave()
  • 过程 4. 当一切的 enter-leave 使命对完结后,group.notify 被调用。假如你留意到它是在后台线手机克隆程中完结的。你能够依据你的需求进行装备。

值得一提的是 wait(timeout:) 选项。它将等候一些时刻来后端开发完结使命,但在超时后,它会继续履行下去。

调度信号量(DispatchSemaphore)

“一个经过运用传统的计数信号来操控跨多个履行环境对资源的拜访的目标。” —— Apple Docs

let semaphore = DispatchSemaphore(value: 1)
semaphore.wait()
task { (result) in
    semaphore.signal()
}

nodejs是干嘛的次拜访某些同享资源时,调用 wait()

当咱们预备释放同享资源时,调用 signal()

DispatchSemaphore上下文语境 value 表示并发使命后端的数量。

Dispat手机耗电快怎么办chWorkItem

一个普遍的观点是,当一个 GCD 使命被安排后,它就不能被撤销。但这是不正确的。由于只要在 iOS8 之前是这样的。

“你想履行的作业,以一种能手机搬家够附加完结句柄或履行依赖项的办法封装。” —— Apple Docs

举个比方,假如你正在运用一个搜索栏。每一个字母的输入都会调用一次查询电影列表的 API。假定你正在输入 “蝙蝠侠”。”B”、”Ba”、”Bat”……每个字母的输入都会触上下文英语发一次网络请求,但事实上咱们不期望这样。咱们后端是做什么的能够简略地撤销之前的调用,例如,假如在那一秒的范围内输入了另一个字母。只要当nodejs和js的区别输入的间隔时刻超过一秒,而用户没有输入新的字母时,那么咱们才以为需求调用那个 API。

当然,假如凭借 RxSwift/Combine 这样的函数式编程,咱手机耗电速度过快怎么办们会有更好的挑选,比方 debounc手机号最旺财的尾数e(for:sc后端是做什么的heduler:options:)。

调度障碍 Dispatch Barrier

Dispatch Barrie手机怎么定位别人手机rs 测验用一个读/写锁来处理这个问题。这确保了只要这个 DispatchWorkIt优先级是什么意思em 会被履行。

“这会使线程不安全目标变得线程安全。” —— Apple Docs

例如,假如咱们要保存游戏,咱们要写到一些翻开的同享文件,资源。

AsyncAfter

咱们能够用如下代码来推迟一些使命的履行:

// 1. Time
let delay = 2.0
// 2. Schedule
DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
    // Execute some task with delay
}

在我看来,就反常而言,这是万恶之源。关于每一个需求推迟的异步使命,我建后端需要学什么议要考虑清楚,假如或许的话,能够运用一些状况办理系统,但不要把这个作为榜首挑选。一般状况下,上下文英语还会有手机搬家其他解法。

(NS)Oper优先级英文ation and (NS)Operation手机管家Qu执行上下文eue

假如你正在运用 NSOperation,这意味着你后端需要学什么在页面逻辑背后运用了 GCD,由于 NSOperation 是后端语言建立在 GCD 之上的。NSOperation 的一些优点是,它有一个更友爱的接口来处理 Dependencies(按特定次序履行使命),它是可调查的(KVO 来调查属性),有暂停、撤销、恢复和操控(你能够指定行列中使命的数量)。

var queue = OperationQueue()
queue.addOperationWithBlock { () -> Void in
    // Background URL 1
    OperationQueue.mainQueue().addOperationWithBlock({
        // Update UI with URL 1 response
    })
}
queue.addOperationWithBlock { () -> Void in
    // Background URL 2
    OperationQueue.mainQueue().addOperationWithBlock({
        // Update UI with URL 2 response
    })
 }

你能够把并发操作数设置为 1,这样它就能够作为一个串行行列作业。

queue.maxConcurrentOperationCount = 1

串行 OperationQueue:

let task1 = BlockOperation {
    print("Task 1")
}
let task2 = BlockOperation {
    print("Task 2")
}
task1.addDependency(task2)
let serialOperationQueue = OperationQueue()
let tasks = [task1, task2]
serialOperationQueue.addOperations(tasks, waitUntilFinished: false)

同步 OperationQueue:

let task1 = BlockOperation {
    print("Task 1")
}
let task2 = BlockOperation {
    print("Task 2")
}
let concurrentOperationQueue = OperationQueue()
concurrentOperationQueue.maxConcurrentOperationCount = 2
let tasks = [task1, task2]
concurrentOperationQueue.addOperations(tasks, waitUntilFinished: false)

组并发优先级最高的运算符 OperationQueue:

let task1 = BlockOperation {
    print("Task 1")
}
let task2 = BlockOperation {
    print("Task 2")
}
let taskCombine = BlockOperation {
    print("taskCombine")
}
taskCombine.addDependency(task1)
taskCombine.addDependency(task2)
let operationQueue = OperationQueue()
operationQueue.maxConcurrentOperationCount = 2
let tasks = [task1, task2, taskCombine]
operationQueue.addOperations(tasks, waitUntilFinished: false)

最终这个是调度组。唯一的区别是,它更容易编写复杂的使命。

调度源(Dis优先级调度算法patchSource)

DispatchSource 用于检测文件和文件夹的改变。针对不同的需求,能够检测不同的改变。篇幅有限,下面我只展现一个比方。

let urlPath = URL(fileURLWithPath: "/PathToYourFile/log.txt")
do {
    let fileHandle: FileHandle = try FileHandle(forReadingFrom: urlPath)
    let source = DispatchSource.makeFileSystemObjectSource(fileDescriptor: fileHandle.fileDescriptor,
                                                           eventMask: .write, // .all, .rename, .delete ....
                                                           queue: .main) // .global, ...
    source.setEventHandler(handler: {
        print("Event")
    })
    source.resume()
} catch {
    // Error
}

死锁(Deadlock)

有一种状况是,两个使命会相互等候对方完结。这被称为死锁。该使命将永手机远不会被履行,并会阻塞应用程序。

// 1. Deadlock
serialQueue.sync {
   serialQueue.sync {
      print(“Deadlock”)
   }
}

千万不要在主行列上调用同步使命,这将导致死锁。

主线程查看器(Main Thread Checke后端开发需要学什么r)

有一种办法能够警告咱们哪里做错了。这是一个非常有用的功用,它能够轻松捕获一些不重要的问题。

假如你在目标上翻开并编辑下一张图片上的方案,优先级是什么意思此刻翻开主线程查看器,那么当咱们在后台做一些 UI 上的更新时,运转时的这个选项会告诉咱们。请看下图中的紫色告诉。

你也能够在 Xcode 终端看优先级表到什么是错误的。关于新手来说,这或许是一个有点古怪的信息,但很快你就会习手机克隆气它。但是你能够在这一行中看到问题所在的办法的称号。

Xcode 中的线程上下文什么意思

在调试的时分,有几个小技巧能够协助咱们。

假如你添加一个断点并在某一行中止。在 Xcode 终端中,你能够输入指令 thread info 它将打印出当时线程的一些细节。

下面是一些对手机搬家终端有用的指令:

po Threa后端开发d.isMainThread

po Thread.isMultiThreade执行上下文d()

po Thread.current

po Thread.main

或许你也有过类似的状手机银行况——当应用程序溃散时,在错误日Node.js志中你能够执行上下文看到诸如 com.alamofire.error.serialization.response 的东西。这意味着框架创建了一些自界说线程,这便是标识符。

Async / Await

在 iOS13 和 Snodejs与java交互wift 5.5 中,引入了人们期待已久的 Async / Await。苹果公司机敏的认识到了一个问题,那便是当新的东西被引入时,在它能够被运用到实践的出产运用前,会有一个很长的缓冲时刻,由于咱们一般需求支撑更多的 iOS 版别。

Async / Await 是一种运转异步代码的办法,不需求回调处理程序。

func exampleAsyncAwait() {
    print("Task 1")
    Task { // 2. Create Task {} Block to be in regular method to handle the Async method 'make'
        let myBool = await make() // 3. Await the method result
        print("Task 2: (myBool)")
    }
    print("Task 3")
}
func make() async -> Bool { // 1. Create method what rsult is async
    sleep(2)
    return true
}

这里有一些nodejs与java交互值得一提的代码:

  • Task.isCancelled
  • Task.init(priority: .background) {}
  • Task.detached(priority: .u手机管家serInitiated) {}
  • Tas手机银行k.canc后端开发工程师el()

我想强调一下 TaskGroup。这是 Awaiting/Asyncnodejs和java性能 国际中的 “DispatchGroup”。我发现 Paul Hudson 在这方面有一个非常好的比方链接。

func printMessage() async {
    let string = await withTaskGroup(of: String.self) { group -> String in
        group.addTask { "Hello" }
        group.addTask { "From" }
        group.addTask { "A" }
        group.addTask { "Task" }
        group.addTask { "Group" }
        var collected = [String]()
        for await value in group {
            collected.append(value)
        }
        return collected.joined(separator: " ")
    }
    print(string)
}
await printMessage()

Actor

Actors 是一个类,它是线程安全的引证类型。它被用来处理数据竞赛和并发问题。正如你鄙人面看到的,拜访 actor 的属性是经过 await 关键字完结的。

“Actors 一次只允许一个使命拜访其可变状况。 ” — Apple Docs

actor TemperatureLogger {
    let label: String
    var measurements: [Int]
    private(set) var max: Int
    init(label: String, measurement: Int) {
        self.label = label
        self.measurements = [measurement]
        self.max = measurement
    }
}
let logger = TemperatureLogger(label: "Outdoors", measurement: 25)
print(await logger.max) // Access with await
// Prints "25"

结论优先级英文

咱们现已聊了许多多线程的论题——从 UI 和后台线程到死锁和 DispatchGroup。我相信你现在现已在成为专家的路上了,或许至少为 iOS 面试中关于多线程主题的问题做好预备。

整个代码示例能够鄙人面的链接中找到:GitHub。我手机管家期望这对你来说是有价值的。

假如发现译文存在错误或手机耗电速度过快怎么办其他需求改善的地方,欢迎到 翻译方案 对译文进行修正并 PR,也可获得相应奖励积分。文章开头的 本文永久链接 即为本文在 GitHub 上的 MarkDown 链接。


翻译方案 是一个翻译优质互联网技术文章的社区,文章来源为 上的英文共享文章。内容掩盖 Android、iOS、前端、后端、区块后端是什么工作链、产品、设计、人工智能等领域,想要查看更多优质译文请持续关注 翻译后端是什么工作方案、官方微博、知乎专栏。

发表评论

提供最优质的资源集合

立即查看 了解详情